Pebble is a watch that has made its name not just because it is a smartwatch that was always designed with a user-focused (and openly available) development kit and environment. Up until recently The Pebble SmartWatch was the biggest ever crowd-funded project on Kickstarter.
The Pebble SmartWatch wasn’t the first, but it is the one that has garnered the most interest because the code is open and available and it means that anybody can download the developer kit and build their own watch faces and applications. In fact, let’s simplify things and just call it software.
Before we dive into the fun process of developing software for your Pebble, you first need to consider what type of application you want to build. Pebble Software falls into two basic classifications:
Faces – A watchface displays the date, time, and any other information that you want displayed on the watch all of the time. This doesn’t mean that there are not interactive or selectable elements, but the simple fact is that a watchface displays information, typical time based with or without other information, with that info permanently displayed.
Apps - Applications can take the form of fully interactive apps, such as a game, partially-interactive apps, such as those targeted for sports or training, or those apps that provide full or partial interfaces to a companion application on your iPhone or Android device.
It should go without saying that there is a certain amount of crossover with these different elements here. For example, some sports apps show the time and your current progress and spend some of their time being a watchface, but in reality all the time it is an app. The same is true if a watchface has a significant amount of interactivity (for example, pressing a button changes the interface).
Within the Pebble the distinction is important for a number of key reasons:
To make it even simpler, if your application requires button presses; it’s an app. Anything else is a watchface.
From a watch perspective, there are differences. Watchfaces appear in the list of Watchfaces within the configuration menu.
<Figure: Configure page>
Also, a watchface can always be selected by using the up/down buttons on the watch while viewing the time. Applications are displayed within the list of applications in the configuration menu.
<Figure: List of Watchfaces>
For me, the distinction is about understanding what you want to display. If you want to simulate a typical watch then it’s a watchface. Anything else is an app.
Regardless of the pontification and official classification, let’s start developing.
I’m not going to repeat the excellent guides and content on getting started that are available at the Pebble developer website. The information offered there will get you started and setup for developing on your chosen platform. I use Mac OS X mostly, but I’ve also developed under Ubuntu for Pebble on a number of occasions.
Start off by following the Installing Pebble SDK on:
Once you have the prerequisite components and the Pebble SDK, you have everything you need to do some basic development.
With the Pebble SDK downloaded you can start to create your own Pebble applications and watchfaces.
You can create your own structure and system for developing your apps, but the Pebble SDK comes with a really cool suite of tools for doing that for you.
Create a directory where you will create your Pebble watchfaces and applications, then use the create_pebble_project.py script to create a project template, specifying the directory where the SDK was created, and the name of the directory where you want the project created:
$ ../Pebble/tools/create_pebble_project.py ../Pebble/sdk first
This creates a directory, first, in the current directory containing a stub watch project.
Tip: Developing Pebble apps sometimes requires a fair amount of developing, building, tweaking, and re-deploying, especially since there isn’t an emulator available. This also breeds a specific type of development effect; that of elements that work one time, but not another, or when you’ve integrated some new feature or enhancement. Using a version control mechanism such as SVN, git, Mercurial or Bazaar will help here. Check in versions that do what you want, and continue to develop. In the event that you break something, you then check out a previous revision that you know works, or compare the current code with a working version (using the diff mode). This is invaluable and will save hours of time (and probably help prevent sleepless nights and premature hair loss).
Most of the directory created when you execute create_pebble_project.py contains symbolic links to the main Pebble developer kit:
$ ll
total 48
lrwxrwxrwx 1 mc staff 36 Aug 18 14:55 include@ -> /Users/mc/Development/Pebble/include
lrwxrwxrwx 1 mc staff 32 Aug 18 14:55 lib@ -> /Users/mc/Development/Pebble/lib
lrwxrwxrwx 1 mc staff 42 Aug 18 14:55 pebble_app.ld@ -> /Users/mc/Development/Pebble/pebble_app.ld
drwxrwxrwx 4 mc staff 136 Aug 18 14:55 resources/
drwxrwxrwx 3 mc staff 102 Aug 18 14:55 src/
lrwxrwxrwx 1 mc staff 34 Aug 18 14:55 tools@ -> /Users/mc/Development/Pebble/tools
lrwxrwxrwx 1 mc staff 32 Aug 18 14:55 waf@ -> /Users/mc/Development/Pebble/waf
lrwxrwxrwx 1 mc staff 36 Aug 18 14:55 wscript@ -> /Users/mc/Development/Pebble/wscript
The two directories are where your custom information goes, src contains the watch source code, and resources contains all the watch specific resources, such as fonts, images and other content required to build the watch.
Source code for a typical Pebble watchface is contained in a single file, although there is no reason it can be expanded beyond this. As a rule, the Pebble SDK and API are quite extensive, and so the source code tends to be quite small and compact. A file, called PROJECT.c based on the project name given when the project was created will exist in the src directory.
For the resources, a JSON file within the resources directory defines the resources that should be embedded into the app. This makes incorporating resources very simple as they can be referred to internally by name. We’ll return to the topic of resources later in the book.
We’ll dig into the source content more thoroughly soon, but it’s worth taking a quick look at the basic structure now.
#include "pebble_os.h"
#include "pebble_app.h"
#include "pebble_fonts.h"
#define MY_UUID { 0x69, 0x26, 0x83, 0xCE, 0x18, 0x1E, 0x4F, 0x83, 0xB9, 0x90, 0xE4, 0xA9, 0xA1, 0x4E, 0x0A, 0x75 }
PBL_APP_INFO(MY_UUID,
"Template App", "Your Company",
1, 0, /* App version */
DEFAULT_MENU_ICON,
APP_INFO_STANDARD_APP);
Window window;
void handle_init(AppContextRef ctx) {
window_init(&window, "Window Name");
window_stack_push(&window, true /* Animated */);
}
void pbl_main(void *params) {
PebbleAppHandlers handlers = {
.init_handler = &handle_init
};
app_event_loop(params, &handlers);
}
The system imports a number of headers; in the default project this contains the standard OS, apps and font information needed by an application:
#include "pebble_os.h"
#include "pebble_app.h"
#include "pebble_fonts.h"
Next a lock defines the application. The MY_UUID should be unique. Internally it is used by the watch to determine the ID of the app. The watch will only store one app per unique ID. That is, if you install two entirely different applications that share the same UUID, only one of them will appear on the watch. The create_pebble_project.py script generates a unique ID each time it is executed. The tool uuidgen will also create a new UUID for you.
$ uuidgen -hdr
// 0F852DA0-338C-4679-8162-8446427C2389
#warning Change the macro name MYUUID below to something useful!
#define MYUUID CFUUIDGetConstantUUIDWithBytes(kCFAllocatorSystemDefault, 0x0F, 0x85, 0x2D, 0xA0, 0x33, 0x8C, 0x46, 0x79, 0x81, 0x62, 0x84, 0x46, 0x42, 0x7C, 0x23, 0x89)
Th macro then defines the application, giving it a name, company ownership, version information, the Icon used to display it within the watch, and the type of application. The last argument specifies the app type. An app uses APP_INFO_STANDARD_APP; a simple watchface uses APP_INFO_WATCH_FACE.
#define MY_UUID { 0x69, 0x26, 0x83, 0xCE, 0x18, 0x1E, 0x4F, 0x83, 0xB9, 0x90, 0xE4, 0xA9, 0xA1, 0x4E, 0x0A, 0x75 }
PBL_APP_INFO(MY_UUID,
"Template App", "Your Company",
1, 0, /* App version */
DEFAULT_MENU_ICON,
APP_INFO_STANDARD_APP);
Next we create a window object. All applications consist of at least one window which is the main UI component used by an app. Some applications will create multiple windows, depending on what is being displayed. There are additional UI components within this, such as layers, for building more complex UIs.
Window window;
Next we define a number of handlers. Handlers are used within the Pebble as the main method for responding to specific events, including everything from building the initial display, updating the display when the time changes, and responding to alarms and button presses.
In this case, we create a very basic window and push that window onto the stack of available windows.
void handle_init(AppContextRef ctx) {
window_init(&window, "Window Name");
window_stack_push(&window, true /* Animated */);
}
Finally, here’s the main function that will run the application. This configures the application handlers will actually execute the application, and then runs the event loop which starts the application running.
void pbl_main(void *params) {
PebbleAppHandlers handlers = {
.init_handler = &handle_init
};
app_event_loop(params, &handlers);
}
Actually, the process is incredibly simple; you create the code that defines the layout and display, you create handlers that react to events including the display update process, and then let the event loop handle the rest of the execution process.
In general then, an application looks like Figure X, a series of handlers reacting to the main loop of events.
<Figure, basic app execution>
Now we understand the code, let’s build it. The ./waf tool within the directory does all the work for you. First you should run the configure step, this configures the projects and creates output directories and other settings:
$ ./waf configure
Setting top to : /Users/mc/Development/Pebble/mywatch/first
Setting out to : /Users/mc/Development/Pebble/mywatch/first/build
Checking for program gcc,cc : arm-none-eabi-gcc
Checking for program ar : arm-none-eabi-ar
'configure' finished successfully (0.047s)
Now the project is configured, it needs to be built:
$ ./waf build
Waf: Entering directory `/Users/mc/Development/Pebble/mywatch/first/build'
[ 1/11] resource_map.json: resources/src/resource_map.json -> build/resources/src/resource_map.json
[ 2/11] app_resources.pbpack.data: build/resources/src/resource_map.json -> build/resources/src/app_resources.pbpack.data
[ 3/11] app_resources.pbpack.table: build/resources/src/resource_map.json tools/pbpack_meta_data.py -> build/resources/src/app_resources.pbpack.table
[ 4/11] resource_ids.auto.h: build/resources/src/resource_map.json tools/generate_resource_code.py build/resources/src/app_resources.pbpack.data -> build/src/resource_ids.auto.h
[ 5/11] app_resources.pbpack.manifest: build/resources/src/app_resources.pbpack.data tools/pbpack_meta_data.py -> build/resources/src/app_resources.pbpack.manifest
[ 6/11] c: src/first.c -> build/src/first.c.1.o
[ 7/11] app_resources.pbpack: build/resources/src/app_resources.pbpack.manifest build/resources/src/app_resources.pbpack.table build/resources/src/app_resources.pbpack.data -> build/app_resources.pbpack
[ 8/11] cprogram: build/src/first.c.1.o -> build/pebble-app.elf
[ 9/11] pebble-app.raw.bin: build/pebble-app.elf -> build/pebble-app.raw.bin
[10/11] inject-metadata: build/pebble-app.raw.bin -> build/pebble-app.bin
[11/11] first.pbw: tools/mkbundle.py build/pebble-app.bin build/app_resources.pbpack resources/src/resource_map.json -> build/first.pbw
{'application': {'crc': 3307898518L,
'name': 'pebble-app.bin',
'reqFwVer': 1,
'size': 312,
'timestamp': 1376837463},
'debug': {'resourceMap': {u'friendlyVersion': u'VERSION',
u'media': [{u'defName': u'DUMMY',
u'file': u'resource_map.json',
u'type': u'raw'}],
u'versionDefName': u'VERSION'}},
'generatedAt': 1376837463,
'generatedBy': 'PTeppic.local',
'manifestVersion': 1,
'resources': {'crc': 1436058892L,
'friendlyVersion': u'VERSION',
'name': 'app_resources.pbpack',
'size': 4292,
'timestamp': 1376837463},
'type': 'application'}
writing bundle to /Users/mc/Development/Pebble/mywatch/first/build/first.pbw
done!
Waf: Leaving directory `/Users/mc/Development/Pebble/mywatch/first/build'
'build' finished successfully (0.388s)
The build extracts and identifies any resources, compiles the code, and then builds and assembles it all into a final file.
If there are problems with the build - missing resources, bad code, typos etc. - they will be notified here.
Once the build has completely successfully a file is created in a build directory called PROJECT.pbw, according to the project name.
Deploying Your First Pebble Watchface
The simplest method of deploying your Pebble app (i.e. without any additional work) is to use the Python SImpleHTTPService to create a basic website straight from your project directory:
$ python -m SimpleHTTPService
This opens up a web service on port 8000 on your machine. Access this from your iOS or Android device from within a web browser; for example by accessing:
http://PTeppic.local:8000/
<Screenshot >
Find the first.pbw file and click on the Pebble app
<Screenshot>
This will open the Pebble application in the corresponding app on your device, and then allow you to deploy it and enable on any connected watches.
<Screenshot>
Another alternative is to set up a simple website, either on your own local machines, or using a public service such as hosting provider or through a service like Dropbox. Then you can copy the pbw file to your website and access it from your phone.
I use this option a lot with a local web server and a script setup that both builds and copies the file onto the webserver so that I can access it from the phone and install it. As I have multiple pebbles, it makes it easy to install it onto the watch I happen to have with me when I’m coding.
Finally, another option is to use libpebble which provides direct access to your Pebble through a Bluetooth serial port on your machine.
$ ./p.py --lightblue --pair get_time
[DEBUG ] LightBlue process has started on pid 82716
[DEBUG ] LightBlue process has started on pid 82760
devicePairingStarted_
devicePairingConnecting_
devicePairingUserConfirmationRequest_numericValue_: 32480
...
devicePairingStarted_
devicePairingConnecting_
devicePairingUserConfirmationRequest_numericValue_: 81188
[DEBUG ] Connection established to 00:18:33:A4:2B:CC
[DEBUG ] Initializing reader thread
[DEBUG ] Reader thread loaded on tid Thread-1
The Pebble ID (given in ‘Connection established’) because it can then be used when installing your app directly to your watch. For example, to distribute an app:
$ ./p.py --lightblue --pebble_id 00:18:33:A4:2B:CC load timezones.pbw
[DEBUG ] LightBlue process has started on pid 82878
[DEBUG ] Connection established to 00:18:33:A4:2B:CC
[DEBUG ] Initializing reader thread
[DEBUG ] Reader thread loaded on tid Thread-1
[DEBUG ] Attempting to add app to bank 1 of 8
[DEBUG ] Sent 4000 of 4292 bytes
[DEBUG ] Sent 4292 of 4292 bytes
If you dont want the app to be launched when it is loaded, use the --nolaunch option.
If you are updating an existing application, use the reinstall option:
$ ./p.py --lightblue --pebble_id 00:18:33:A4:2B:CC reinstall timezones.pbw
Of all the available methods this is the simplest and most effective when developing applications as it enables you to very simply update, rebuild, and redeploy without requiring your phone to complete the installation.
At the time of writing, there is no easy way to create a screenshot from your running Pebble watch. There are some efforts to make such tools available, and you may want to try out the httpebble app which works on your iOS or Android device and connects to your Pebble watch to exchange using HTTP. An extension by Edward Patel enables the screenshot functionality. For the purposes of the book I’ll use a photo of the image.
There has been error in communication with Booktype server. Not sure right now where is the problem.
You should refresh this page.