Ad

Our DNA is written in Swift
Jump

WWDC iWatch with Pebble

There will most likely not be an iWatch this year, the Pebble is what comes closest to it. It does have an internal ARM-based CPU and it can communicate with iOS devices over bluetooth. There are two kinds of of things you can build for it: watch faces (which don’t have any interaction) and apps.

For today’s project I want to try to put together a watch face that shows the WWDC 2013 Logo and a countdown to the keynote on Monday, June 10th.

Update June 2nd: I made the UTC offset changeable and released a build of the app via mypebblefaces.com

The first thing you need to decide is whether you want to make a watch face or an app. The Pebble Developer gives these pointers:

  • If your goal is to show the time in some unique way then you will develop a watch face app.
  • If you want to display information that the wearer is able to read all the time then you will also develop a watch face app.
  • If your goal is to provide specialised functionality that requires button interaction then you will be developing a standard app.

So, in other words, if you’re only displaying something (like the keynote countdown) you want to make a watch face app, so this is what we are going for.

You can also create iOS apps that interact with the Pebble using the Pebble iOS SDK. But this is outside of the scope of this first experiment.

Installing Apps

In order to be able to install apps on your Pebble you need to have the Pebble iOS app installed and your Pebble paired over Bluetooth with your iPhone. The Pebble app registers as handler for the pbw file format. If you download such a file in Safari or have it in an e-mail then you can “Open In…” with the Pebble app. This will then install it over bluetooth on the watch.

The documentation links to a collection of watch faces which you can install via your Mobile Safari.

Note that you only have 8 slots available for apps, so if you fill these up you cannot install any additional apps without first removing one. This can also be done via the Pebble app.

The readme in the 1.1.1 SDK mentions a size limit for app binaries of 24 KB and app resources of 96 KB.

Development Prerequisites

Homebrew

Because of the “Unixy tools in the toolchain” you have to develop Pebble apps either on Mac OS X (10.7 and above) or Ubuntu (12.04LTS or above). I’ll of course do it on Mac.

For compiling apps we need gcc which is part of the Command Line Tools which Apple provides as an additional download for Xcode. Xcode – Preferences – Downloads – Components: Install.

The second prerequisite is that you install Homebrew which calls itself “The missing package manager for OS X”. On this page you have to scroll down a little to where it says “Install Homebrew”. It basically just execute a ruby script that takes care of everything. When prompted for your admin password, enter it.

ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

Right after this has finished they say you should execute the following and “fix any problems it identifies”.

brew doctor

I got a warning that XQuartz was outdated with version 2.7.3 and I should install XQuartz 2.7.4. I don’t think that this really matters for compiling Pebble apps, but this newer version can be downloaded from the XQuartz web site. So I did that.

After installing the newer version from XQuartz and running doctor a second time, I received a satisfying “Your system is ready to brew.”

MPC Library

Next we install the mpc library via homebrew.

brew install libmpc

It seems of the utmost importance to have the correct libmpc version installed. They recommend this command to verify:

ls /usr/local/lib/libmpc.3.dylib

If this command returns a result then you are good.

ARM CS Tools

Depending on your OS X version you get the 40 MB arm-cs-tools for 10.8 or 10.7. You just double-click on the downloaded file and end up with a arm-cs-tools folder.

Create a pebble-dev folder under your user folder and move this into it.

mkdir ~/pebble-dev
mv ~/Downloads/arm-cs-tools ~/pebble-dev/

You need to add this path to your PATH environment variable. In my case I have that in ~/.profile:

alias svn='xcrun svn'
alias git='xcrun git'
alias make='xcrun make'
PATH=$PATH:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/oliver/java/gradle-1.6/bin:/~/pebble-dev/arm-cs-tools/bin

To test the setup open a new shell (CMD+N – so that the changes to the PATH take effect). and do:

arm-none-eabi-gcc --version

If you see the ARM EABI Toolchain copyright info then you’re good.

Python, PIP, Freetype

Verify that you have Python 2.7 installed. If I remember correctly then this comes with the OS. If not, you can get that from python.org.

python --version

And then we install PIP. (I needed to run this twice because the first mirror responded with an error, the second succeeded)

sudo easy_install pip

Freetype. (Note that on 10.7 there’s an extra command you need to run to make it available. Not so on 10.8)

brew install freetype

Pebble Smartwatch SDK

Get the current SDK release from Pebble. Their guide assumes that we put everything in pebble-dev under the user home folder. So for sake of simplicity we do the same.

A bit confusing is that they changed the naming from pebble-sdk-release-001.zip to PebbleKit-v1.1.1.tar.gz. Let’s hope that this is the only difference.

We download the file, unzip it and move the resulting folder PebbleKit-v1.1.1 into our previously created ~/pebble-dev.

~/Downloads/PebbleKit-v1.1.1 ~/pebble-dev/

Apparently the next step involves an sdk/requirements.txt file which is no longer at the root of the SDK folder, but one level down in Pebble.

We install the Python Library dependencies thusly:

cd ~/pebble-dev/PebbleKit-v1.1.1/Pebble/
pip install --user -r sdk/requirements.txt

When doing that I got enormous amounts of warnings, but still at the end it said

Successfully installed Pillow freetype-py sh
Cleaning up...

So we’re keeping our fingers crossed that this indeed meant that we have been successful.

At this point we can verify that freetype has been installed correctly with this command:

python -c "import freetype"

If you get no error then all is well.

At this point the documentation states:

It’s time to build a watch face app from source code!

Yeah, about friggin’ time!

Test Building a Watch Face

Before we dive into actually creating our own watch face, the documentation mentions “2.5 Build a watch from source code”.

./waf configure
./waf build

Note the second line from the bottom where it tells you the output location of the bundle, ~/pebble-dev/PebbleKit-v1.1.1/Pebble/sdk/build/ in my case.

I was able to install the pbw by emailing it to my iPhone and then going “Open In…”; a much geekier method is to start a quick HTTP server from the current folder and then browsing that from Safari on your iPhone.

python -m SimpleHTTPServer 8000

On my iPhone I then opened http://BigDrops:8000 with BigDrops being the name of my MacBook (as you can see in the Sharing prefpane). You can navigate the folder structure and when you tap on the sdk.pbw file you get the same result as with “Open In”.

Side Note: Nice Trick! (The one with the ad-hoc Web Server)

Creating a new Pebble App Project

There is an enormous amount of stuff needed to set up to get a project to build, something that maybe somebody should create an Xcode project template for.

At this time a Python script is provided to set up a new project folder for you with all it needs to start. So I moved into my usual Project folder ~/Documents/Projects and there I called this script:

 ~/pebble-dev/PebbleKit-v1.1.1/Pebble/tools/create_pebble_project.py ~/pebble-dev/PebbleKit-v1.1.1/Pebble/sdk WWDC2013_Countdown_Watch

The first parameter is the root folder of the Pebble sdk, the second is the name of the project folder you want to create. This sets up the project such that you only need to change into the new directory, waf configure and build and you end up with a WWDC2013_Countdown_Watch.pbw in the build subfolder.

In the src sub folder this creates an empty app for us (not a watch face). Let’s inspect that and tweak a few standard values.

#include "pebble_os.h"
#include "pebble_app.h"
#include "pebble_fonts.h"
 
// the app's UUID
#define MY_UUID { 0x6B, 0xF0, 0x43, 0xB0, 0xFD, 0x06, 0x48, 0x3D, 0x84, 0x7E, 0xBC, 0x9B, 0x08, 0xAD, 0x58, 0xA0 }
 
PBL_APP_INFO(MY_UUID,
             "WWDC 2013", "Cocoanetics.com",
             1, 0, /* App version */
             DEFAULT_MENU_ICON,
             APP_INFO_STANDARD_APP);
 
Window window;
 
void handle_init(AppContextRef ctx)
{
  (void)ctx;
 
  window_init(&window, "WWDC 2013 Countdown");
  window_stack_push(&window, true /* Animated */);
}
 
void pbl_main(void *params)
{
  PebbleAppHandlers handlers = {
    .init_handler = &handle_init
  };
  app_event_loop(params, &handlers);
}

Apparently each app needs to have a UUID. The setup script has created one for us, but you can always create a new one with the uuidgen utility.

I also modified the app name, my company name and the window name. This app can be installed like the test app we installed earlier. At first I thought I had made a mistake since it didn’t appear as one of the watch faces you can cycle through with the top and bottom right buttons.

The Pebble App Info contains one or more app flags which you can combine to inform the watch about how to display the app:

  • APP_INFO_STANDARD_APP – application is not a watchface, but a “standard” app. The system will show the app in the main menu.
  • APP_INFO_WATCH_FACE – application is a watchface. The system will show the app in the watchfaces menu.
  • APP_INFO_VISIBILITY_HIDDEN – hide the application
  • APP_INFO_VISIBILITY_SHOWN_ON_COMMUNICATION – hide the application, unless there is ongoing communication with the companion smartphone application

The two kinds of apps (as mentioned before) have the corresponding defines: APP_INFO_WATCH_FACE for watch faces and APP_INFO_STANDARD_APP for standard apps.

To have the app show in the Watchfaces menu (and also be available for quick selection via the buttons) we change the type to APP_INFO_WATCH_FACE. There is relatively little space for the name of the face, so I just call it “WWDC 2013”.

Adding a Watchface Icon

To know how the icon image should look like we inspect another demo app’s icon. The usual size seems to be 24 x 28 pixels. The build process automatically converts grayscale images to black and white, transparency (if present) gets preserved.

I found a small Apple logo at the top right of the developer site, this already had the correct dimensions. All I needed was to cut it out and give it a white background. The opaque background is necessary because all regions that are transparent become black in the menu.

Mini Apple Logo

The build process requires  a JSON file that specifies all the resources to add to the the app bundle. This is located in resources/src/resource_map.json. The structure and what the fields mean you can see in the SDK documentation.

{"friendlyVersion": "VERSION",
 "versionDefName": "APP_RESOURCES",
 "media": [
        {
        "type": "png",
        "defName": "IMAGE_MENU_ICON",
        "file": "images/menu_apple_logo.png"
        }
 ]
}

To have the app use this icon we put RESOURCE_ID_ in front of the defName IMAGE_MENU_ICON and set this as the parameter to the left of APP_INFO_WATCH_FACE.

Result! (I almost wept.)

Apple-lovin' Pebble

Next up we want to add some code to actually have the watchface DO something. Right now it is just a blank screen

Cracking the Code

The basic structure of a Pebble app is also explained in the SDK, but I found it hard to understand it just from reading the explanations. Suffice it to say that there there are a couple of concepts we have heard before.

You need a main window which in the above sample is created as a static variable. And you have a run loop which executes while the app is running. You specify what kinds of events the app should react to by installing handlers for these. At the most basic level you have only an init handler.

Available event handlers are, according to pebble_os.h:

typedef struct 
{
  PebbleAppInitEventHandler init_handler;
  PebbleAppDeinitEventHandler deinit_handler;
  PebbleAppRenderEventHandler render_handler;
  PebbleAppInputHandlers input_handlers;
  PebbleAppTickInfo tick_info;
  PebbleAppTimerHandler timer_handler;
  PebbleAppMessagingInfo messaging_info;
} PebbleAppHandlers;

The sample app only sets the init_handler to the address of a handle_init(ctx) function. In this handler it initializes the window variable and pushes it onto the window stack.

For a watch face you probably want to update the display every second to show the new time, or in our case to decrease the countdown.

On a Window you can mount multiple layers. Specialized layers for displaying an image (from your resources) are available, so are text layers. For my initial project I am using a BmpContainer to display the logo at the top, a center TextLayer for the number of days and at the bottom another TextLayer for the hours, minutes and seconds ticking away.

For the heck of it I also got myself TTF versions of Myriad Pro, Apple’s classical font. The center label uses the bold variant, the bottom one the regular one. I’m also thinking of maybe replacing the WWDC 2013 text with labels as well since the scaled down image looks a bit “unclean”.

I’m not going into details about the programming here. Suffice it to say that you’ll be missing the convenient environment of Cocoa. You don’t even have any proper date arithmetic functions. Indeed many C-functions that you would usually use are not supported on Pebble since they need malloc. Pebble doesn’t do malloc if I understood this correctly.

So to get the time interval from now until the WWDC 2013 keynote I had to use a really really dirty trick which only works since we already have June. Also there is no sprintf for the same reason. I found a minimal implementation of it created by Michal Ludvig for use in embedded projects. This allowed me to format the output for the text labels … pardon layers.

Result

The code can be found on my Pebble GitHub repository. Please forgive me if it is not polished so far, that’s something to maybe do while we wait for the Keynote countdown to tick to zero.

Conclusion

Instead of going through all the pain of installing everything on your local machine, there is CloudPebble which lets you “build your watchfaces and apps without installing anything”.

Programming something for Pebble means you have to let go of all convenience you have when making iOS apps. You are limited to very very basic C plus a few necessary functions that are provided by the Pebble SDK.

Also the display which exists only of pixels that are either on or not needs some getting used to, having looked at your beloved Retina iOS displays until now. Finally setting up the SDK takes a dedicated person to not give up on.

But if you are a tinkerer and don’t mind working on the command line without a simulator then Pebble is good for a few fun projects.


Categories: Recipes

29 Comments »

  1. Great write-up, i am messing with your example code and it seams to think you are always in the same month, for example it just subtracts the number of days left to reach the same day that month, it doesnt add the other days left for each month until that date. How would I do that ?