Pebble is a "smart" watch developed by Pebble Technology Corporation. It’s one of the most successful Kickstarter projects to-date and has received significant success with consumers. The Pebble watch comes with a black-and-white e-paper display, and includes several sensors such as a magnetometer, an ambient light sensor, and an accelerometer. The Pebble watch supports the following types of applications:

  • Standalone apps that run on the Pebble
  • Standalone apps that run on the Pebble and can communicate with either an iPhone or Android app
  • Standalone apps that run on the Pebble and can communicate with a Javascript application running on a mobile device
  • Sports apps that run on iPhone or Android devices and send the data directly to the Pebble watch

In this article, I’ll walk you through the process of creating a Pebble application and how you can extend its functionality by writing JavaScript code to connect it to the outside world.

Pebble Specifications

The Pebble comes in two varieties: a classic "plastic" Pebble ($150) and a classier Pebble Steel ($249) (see Figure 1).. Other than the look (and a bit more memory), the Pebble Steel is identical to the classic Pebble.

NOTE: Images of the Pebble watch are from the Pebble website:

Figure 1: The Pebble and the Pebble Steel

The primary way of interacting with the Pebble is through its four buttons; there’s one on the left (the Back button), and three on the right (Up, Select, and Down). You can see this in Figure 2.

Figure 2: The four buttons on the Pebble

In terms of hardware specs, the Pebble has the following:

  • A 1.26" display containing 144 x 168 pixels
  • Black-and-white e-paper display
  • 4MB flash memory for the classic Pebble and 8MB flash memory for the Pebble Steel
  • An ARM Cortex-M3 processor running at maximum 120Mhz
  • A 140mAh battery that can last five to seven days of normal usage on a single charge

In terms of connectivity, the Pebble supports only Bluetooth, albeit two types of Bluetooth, both Classic and Low Energy (LE). When you pair up the Pebble with your mobile device, there will be two types of Bluetooth connections if your mobile device supports Bluetooth LE. The Classic Bluetooth connection is used by mobile device to install apps on your Pebble, as well as for delivering caller IDs, remote music control, updates, etc. The Bluetooth LE connection, on the other hand, supports iOS 7’s Apple Notification Center Service (ANCS) for sending notifications over Bluetooth LE.

Connecting the Pebble to the Mobile Device

As the Pebble has no network connectivity options apart from its Bluetooth connections, it must rely on your mobile device to communicate with the outside world. It does so through the Pebble Mobile app. At this time, the Pebble Mobile app is available on the iOS and Android platforms.

The role of the Pebble Mobile app is to:

  • Install applications (the binary of a Pebble app has the .pbw extension) onto the Pebble
  • Provide the Pebble App Store and allow users to browse and install apps (see Figure 3) directly on the Pebble
  • Load and unload apps from the Pebble onto the App Locker (there is a maximum of eight apps that can be installed on the Pebble at any one time) (see Figure 4)
  • Provide a development platform where developers can deploy applications onto the Pebble
Figure 3: The Pebble App Store provides the central repository for Pebble apps.
Figure 4: The Pebble Mobile app manages the application installed on the Pebble.

Figure 5 shows the role played by the Pebble Mobile app.

Figure 5: The roles played by the Pebble Mobile App

This last point is of interest to developers. For deploying Pebble apps, the Pebble Mobile app acts as a server and the developer deploys the Pebble app through it. Both the mobile device (iOS or Android devices hosting the Pebble Mobile app) and the development computer must be on the same WiFi network.

Preparing Your Mobile Device

For this article, use an iPhone to host the Pebble Mobile app. Once the Pebble Mobile app is installed and paired with the Pebble, go to the Settings page, tap on the Pebble app, and turn on Developer Mode (see Figure 6).

Figure 6: Turning on the Pebble Developer Mode

Once the Developer Mode is turned on, you will be able to see the DEVELOPER option (left Figure 7; turned off by default). Tap the option and enable it (right Figure 7).

Figure 7: The IP address of the Pebble Mobile app

Once the Developer Mode is enabled, the Pebble Mobile app acts as the server for deploying the application onto your Pebble. Observe the IP address of the server. This is the IP address of your Pebble mobile app as well as your iPhone.

Developing for Pebble

There are two ways to develop for Pebble:

  • Download and install the Pebble SDK from: . The SDK is Unix-based and requires you to use a Mac or a virtual computer, such as Virtual Box.
  • Use the cloud-based CloudPebble ().

For this article, I used the SDK provided by Pebble.

Installing the SDK

The easiest way to install the Pebble SDK is to use a Mac and enter the following command in the Terminal window:

curl -sSL | sh && source ~/.bash_profile

If everything installs correctly, you should see a pebble-dev folder in your home directory. If not, check out the installation help provided by Pebble at: .

To test the Pebble SDK, issue the following commands:

MyMac:~ wei-menglee$ cd pebble-dev
MyMac:pebble-dev wei-menglee$ pebble new-project
Creating new project HelloWorld

The above commands change the working directory to the pebble-dev directory and create a new Pebble project named HelloWorld. Once the project is created, you’ll be able to see a folder named HelloWorld. Within this folder, you’ll be able to see the files shown in Figure 8.

Figure 8: The folder containing the HelloWorld Pebble project

The use of the various files and folders are:

  • appinfo.json: contains the application’s information, such as application name, icons, etc.
  • resources: a folder containing the applications resources, such as images, etc.
  • src: a folder containing the application’s source code
  • HelloWorld.c: the main Pebble source code
  • Wscript: information for the compiler compiling the Pebble app

To build the project, change the directory to the folder containing the project use the pebble build command, like this:

MyMac:pebble-dev wei-menglee$ cd HelloWorld/
MyMac:HelloWorld wei-menglee$ pebble build

If the compilation is successful (you will know if it isn’t), you’ll find a new folder called build within the project. Within the build folder is a file with the .pbw extension. This is the deployable Pebble application that you can send through email to someone with a Pebble watch to install through the Pebble Mobile app. For now, let’s learn how to deploy this application directly onto your Pebble watch. Issue the following command:

MyMac:HelloWorld wei-menglee$ pebble install --logs --phone <ip_address_of_your_mobile_phone>
[INFO ] Enabling application logging...
[INFO ] Installation successful
[INFO ] I app_manager.c:138 Heap Usage for <Timely>:
  Total Size <6364B> Used <5524B> Still allocated
[INFO ] D HelloWorld.c:89 Done initializing, pushed
  window: 0x2001a5d8
[INFO ] Displaying logs ... Ctrl-C to interrupt.

The Pebble application is now deployed to your Pebble watch through the Pebble Mobile app.

When deploying an application onto your Pebble, ensure that the Pebble Mobile app on your mobile device is running in the foreground.

Figure 9 shows the application running on the Pebble watch. Press any of the three buttons on the right and observe the message printed on the screen.

Figure 9: The HelloWorld application on the Pebble

Examining the HelloWorld Example

Now that you’ve run the HelloWorld application on your Pebble, let’s take a look at how it works. Listing 1 shows the content of the HelloWorld.c file. Instead of explaining the code line-by-line, I’ve added comments throughout the code to explain how it works.

Here are some of the important points:

  • Pebble uses the C programming language. There are no classes and objects in C, and in Pebble programming, you deal mainly with value types, such as int, float, char, and structures.
  • You often need to deal with pointers. A pointer is a variable that contains the address of another variable and "points" to that variable in memory. A pointer has the following syntax: variable_type *pointer_name;
  • A Pebble app contains one or more Windows. A Window contains the UI of your application. Windows are added to a stack, and only the topmost window is visible at any time.
  • You add UI elements to a Window’s layer. The various layers of a Window make up the UI of your Pebble application. One example of such a layer is the TextLayer, which allows you to display text.
  • The Pebble SDK comes with a number of functions that lets you create new structures. An example would be the text_layer_create() that creates and returns a pointer to a TextLayer structure. The best way to explore these functions is to check out the documentation using the Pebble documentation located at .
  • You need to perform your own memory management. In general, if you create something using functions that end with the _create name, such as text_layer_create() and window_create(), you need to free up the memory using a corresponding _destroy function, such as text_layer_destroy() and window_destroy(). Failure to call the corresponding destroy function results in a memory leak.

To exit from the Pebble application that’s currently running on your Pebble, press the Back button. In the Terminal window, you should be able to see the output from the Pebble:

[INFO ] I app_manager.c:138 Heap Usage for <HelloWorld>: Total Size <23084B> Used <204B> Still allocated <0B>

In particular, look for the part where it says: Still allocated <0B>. If you see a number other than zero, it means that there is a memory leak.

Creating Your Own Custom Watch

With the obligatory HelloWorld application out of the way, let’s now create something useful from the APIs provided by the Pebble SDK. For this exercise, you’ll create your own clock that displays the current time. First, issue the following command to create a new project named MyWatch:

MyMac:pebble-dev wei-menglee$ pebble new-project

Displaying the Current Time

The next thing to do is to display the current time of the Pebble app. Edit the MyWatch.c file located in the src folder of the project and add in the code highlighted by comments shown in Listing 2.

Here is what the application does:

  • It uses the Tick Timer Service to call your app every time a component unit (such as second, minute, hour, day, month, and year) changes.
  • It subscribes to a tick timer event service using the tick_timer_service_subscribe() function and passes it the time component unit,and the event handler.
  • It unsubscribes from a tick timer event service, using the tick_timer_service_unsubscribe() function.
  • The handle_second_tick function fires every second and the current time is passed in via a tm structure.
  • In order to print out the current time, it needs the components (hour, minute, second) converted into their string equivalents. You can do this using the snprintf() function.

Deploy the application to the Pebble by building it and then installing it. You should then see the application displaying the current time (see Figure 10) and updating every second.

Figure 10: The application displays the current time.

Inverting the Screen

Sometimes the user might prefer the application to have a black background (with white text). You can easily achieve this by using the InverterLayer layer. Add the comment-highlighted code to the MyWatch.c file as shown in Listing 3.

The InverterLayer inverts the graphics context for all the layers that are "behind" it; it changes all the black pixels to white and vice versa. The InverterLayer is useful for cases where you need to highlight the selection of an item in a menu. To switch back to the original white background, you remove the InverterLayer. Figure 11 shows how the application looks when the background of the application is inverted (when you press the Up button on the Pebble).

Figure 11: The application with the screen inverted

Displaying the Application Menu Icon

Next, modify the application to display an icon on the application menu. The application menu is where users navigate through the list of applications available on the Pebble (see Figure 12).

Figure 12: The Pebble’s Application Menu

As the Pebble’s screen is only black and white, you need to prepare an image that is also black-and-white. Also, for the application menu icon, the image size must be 28x28 pixels.

The Pebble SDK automatically converts color or grayscale images into black and white bitmaps. I recommend that you convert it yourself using your own image-processing software, such as HyperDither.

Once you’ve prepared the image, create a folder named images within the resources folder of the project and drag and drop the image into it (see Figure 13).

Figure 13: The icon in the images folder within the resources folder

You also need to modify the appinfo.json file to inform the compiler that you are setting an application menu icon. Add the code shown after the media attribute in the following snippet:

  "uuid": "ad7b91ec-3b37-4e14-950d-2ebff36295fe",
  "shortName": "MyWatch",
  "longName": "MyWatch",
  "companyName": "MakeAwesomeHappen",
  "versionCode": 1,
  "versionLabel": "1.0.0",
  "watchapp": {
    "watchface": false
  "appKeys": {
    "dummy": 0
  "resources": {
    "media": [
        "type": "png",
        "menuIcon": true,
        "name": "IMAGE_MENU_ICON",
        "file": "images/icon.png"

That’s it! Build the application and then deploy it onto the Pebble. When you go to the application menu now, you’ll see the icon displayed next to your application name (see Figure 14).

Figure 14 : The application menu icon displayed next to the application name


Besides displaying images, Pebble also supports the drawing of common shapes, such as circles, rectangles, paths, lines, etc. Let’s now add the highlighted code to the MyWatch.c file shown in Listing 4.

To draw on the Window, you need to create a Layer. The layer_set_update_proc() function allows you to set a function to be called automatically whenever the layer needs to be redrawn. In this case, the function is called my_layer_update_proc(). In the my_layer_update_proc() function, you draw the first two rectangles to represent the battery, and a third rectangle to represent the battery level (see Figure 15).

Figure 15 : Use rectangles to represent a battery.

The width of rect3 is set to 50 pixels for a full battery level. Figure 16 shows how the drawings look when you deploy the application onto the Pebble.

Figure 16 : The drawings representing the battery level

When the screen is inverted, it looks like Figure 17.

Figure 17 : The screen when inverted

Monitoring the Battery Level

Now that you’ve drawn the rectangles representing the battery, add in the code to monitor the battery level of the Pebble. Add the highlighted code to the MyWatch.c file as shown in Listing 5.

The BatteryChargeState structure contains three members:

  • charge_percent allows you to know how full the battery is (0 to 100).
  • is_charging allows you to know if the battery is currently being charged.
  • is_plugged allows you to know if the charger cable is connected.

To know the current battery state, use the battery_state_service_peek() function. To subscribe to changes in the battery level, use the battery_state_service_subscribe() function and pass it a function name. In this case, the function is battery_state_handler. To refresh the drawings, call the layer_mark_dirty() function and pass it to the layer that you wish to redraw. This function automatically calls the my_layer_update_proc() function to update the drawings representing the battery. Deploy the application onto your Pebble and you‘ll be able to see the battery level.

The maximum size of a Pebble app is 100KB, including the app’s resource and binary files.

Converting the Application into a Watchface App

By default, your application (known as a watch app) allows interaction with the user through the three buttons on the right edge of the Pebble. However, there are times when the user doesn’t need to interact with your application, which runs without user input. This is known as a Watchface app. A Watchface app is designed to run for a long period of time, and usually provides information (such as time, weather, etc.) at a glance.

To convert your application from a watch app to a Watchface app, set the watchface attribute in the appinfo..json file to true and rebuild the application:

  "uuid": "ad7b91ec-3b37-4e14-950d-2ebff36295fe",
  "shortName": "MyWatch",
  "longName": "MyWatch",
  "companyName": "MakeAwesomeHappen",
  "versionCode": 1,
  "versionLabel": "1.0.0",
  "watchapp": {
    "watchface": true
  "appKeys": {
    "dummy": 0
  "resources": {
    "media": [
        "type": "png",
        "menuIcon": true,
        "name": "IMAGE_MENU_ICON",
        "file": "images/icon.png"

When the application is deployed to the Pebble, you’ll realize that the system status bar (at the top) is now gone (see Figure 18). What’s more, it can now be a default app when the application menu times out. However, pressing the Up or Down button switches between the other Watchface apps on the Pebble and pressing the Select button invokes the application menu.

Figure 18 : The application is now running as a Watchface app.

Interacting with the Outside World

As discussed earlier, the only connectivity option the Pebble has is its Bluetooth connection to the mobile device. It doesn’t have GPSor WiFi, nor does it have cellular network connectivity. The only way for your Pebble application to communicate with the outside world is through the mobile device.

A Watchface app is designed to run for a long period of time, and usually provides information (such as time, weather, etc.) at a glance. Unlike a watch app, a Watchface app does not interact with the users through the three buttons.

There are a couple of options available. Figure 19 shows the first option: writing native apps on iPhone or Android devices. These native apps can use the PebbleKit for iOS or the PebbleKit for Android Frameworks to communicate with the Pebble. Using this approach, your native app can connect to the Internet and consume services, connect to socket servers, etc., and relay the data back to the Pebble.

Figure 19 : Write a native app to help the Pebble app communicate with the outside world

Although this option sounds interesting, it requires you to write native apps for both iOS and Android.

Beginning with Pebble 2.0, you can now write your code using a feature known as PebbleKit for Javascript. Using the PebbleKit for JavaScript, you write your Pebble app in C and an additional app in JavaScript. Here is how it works:

  • Bundle your JavaScript code together with your Pebble app.
  • The Pebble Mobile app extracts and installs the JavaScript code during app installation.
  • The JavaScript code runs within a sandbox inside the Pebble Mobile app on the Android or iOS device.
  • Your Pebble app starts a request to run the JavaScript code on the phone; JavaScript code is killed when your Pebble app exits.
  • The JavaScript code can perform tasks such as consuming Web services, geo-location, persistence storage, etc.

Figure 20 summarizes the flow of activities between the Pebble app and the JavaScript code.

Figure 20 : The Pebble app communicates with the JavaScript code

The advantages of using the PebbleKit for JavaScript are:

  • It’s completely platform-independent. You don’t have to write separate native code for Android and iOS.
  • Your app is easy to install; users need not download additional mobile apps.

Your JavaScript code can:

  • Access Internet and GPS on the phone
  • Use the phone screen and keyboard to display a configuration interface
  • Persist data on the phone
  • Do other things that you can do with JavaScript

Using PebbleKit for JavaScript

The best way to understand how PebbleKit for JavaScript works is to create a simple example. In Terminal, type in the following command to create a project named helloJS:

MyMac:pebble-dev wei-menglee$ pebble new-project
--javascript helloJS

Examine the helloJS folder created. Besides the usual suspects, you should also see a folder named js that contains a file named pebble-js-app.js (see Figure 21).

Figure 21: The JavaScript code file inside the Pebble project

Defining the Keys

In order to facilitate communications between the Pebble app and JavaScript code, data is exchanged using key/value pairs. You first need to define the keys that you’re going to use in your app.

Add the following statements to the appKeys attribute in the appinfo.json file:

  "uuid": "0ada7161-4ce5-4a88-9332-8b93b9a9d9cc",
  "shortName": "helloJS",
  "longName": "helloJS",
  "companyName": "MakeAwesomeHappen",
  "versionCode": 1,
  "versionLabel": "1.0.0",
  "watchapp": {
    "watchface": false
  "appKeys": {
      "key1": 5,
      "key2": 6,
      "key3": 7,
      "key4": 8
  "resources": {
    "media": []

In the above snippet, you defined the keys that you want to use in your application. In this example, to send data using a key/value pair, you can either use "key1" or 5 as the key. Similarly, you can either use "key2" or 6 as the key for another key/value pair, and so on. The values 5, 6, 7, and 8 used in this example are purely arbitrary; you can use any numbers you want for the keys. The use of either the string "key1" or the number 5 will be evident later on when you examine the code for sending and receiving data.

Sending a Message from the Pebble to JavaScript

The first thing you need to learn is how to get the Pebble app to send a message to the JavaScript code running on the Pebble Mobile app. Add the highlighted statements to the helloJS.c file as shown in Listing 6:

In order for your Pebble app to communicate with the JavaScript code running on the Pebble Mobile app, you need to define a few functions to receive and send data:

  • Use the app_message_register_inbox_received() function to register a handler for incoming messages.
  • Use the app_message_register_inbox_dropped() function to register a handler for dropped incoming messages.
  • Use the app_message_register_outbox_sent() function to register a handler for messages that are sent successfully.
  • Use the app_message_register_outbox_failed() function to register a handler for messages that have failed to send.
  • Use the app_message_open() function to open a bi-directional communication channel between the Pebble app and the Pebble Mobile app, passing it the required size for the Inbox and Outbox buffers.

To send data to the JavaScript code, you use a DictionaryIterator structure. A DictionaryIterator structure contains key/value pair Tuplets (a Tuplet is a helper data structure to create a Tuple or key/value pair). You use the app_message_outbox_begin() function to begin writing to the outbox’s Dictionary buffer. You use the app_message_outbox_send() function to send a DictionaryIterator variable.

Receiving Messages on JavaScript

To receive a message on the Javascript side, add the highlighted statements to the pebble-js-app.js file located in the js folder of the src folder (see Listing 7).

When the Pebble opens the communication channel to communicate with the JavaScript, the JavaScript code receives the "ready" event. When a message is received on the JavaScript, it receives the "appmessage" event. You can retrieve the data that is received on the JavaScript side using the format: e.payload.<keyname>.

Earlier, you defined four keys: key1 (value 5), key2 (value 6), key3 (value 7), and key4 (value 8). The string values of the keys ("key1", "key2", etc.) are used in JavaScript, while their corresponding values (5, 6, etc.) is used in the C code.

Build and deploy the application onto the Pebble and observe the output:

MyMac:helloJS wei-menglee$ pebble install --logs
[INFO ] Enabling application logging...
[INFO ] Installation successful
[INFO ] I app_manager.c:138 Heap Usage for
  <MyWatch>: Total Size <21960B> Used <364B> Still
  allocated <0B>
[INFO ] D helloJS.c:95 Done initializing, pushed
  window: 0x2001a748
[INFO ] Displaying logs ... Ctrl-C to interrupt.
[INFO ] JS: starting app: 0ADA7161-4CE5-4A88-9332-
  8B93B9A9D9CC helloJS
[INFO ] app is ready: 1
[INFO ] JS: helloJS: Hello world! - Sent from your
javascript application.

The output shows that the Ready message was received by the JavaScript. Press the Select button on the Pebble and observe the output printed in the Terminal application:

[INFO ] JS: helloJS: [JAVASCRIPT] Received message
  from PEBBLE: {"key2":"A string","key1":35}
[INFO ] JS: helloJS: [JAVASCRIPT] ---key1: 35
[INFO ] JS: helloJS: [JAVASCRIPT] ---key2: A string
[INFO ] D helloJS.c:7 [PEBBLE] Message delivered

The output shows that the message was sent successfully from the Pebble to the JavaScript. The JavaScript has also successfully received the two key/value pairs.

Sending a Message from JavaScript to Pebble

The next step is for the JavaScript to send back a message to the Pebble. For example, the JavaScript may consume a Web service and return the result to the Pebble.

Add the highlighted statements to the pebble-js-app.js file (see Listing 8).

To send a message back to the Pebble app, use the Pebble.sendAppMessage() function. The data to be sent back is formulated as a JSON string.

Receiving Messages on the Pebble

To receive the message sent by the JavaScript to the Pebble app, add the highlighted statements to the helloJS.c file (see Listing 9):

When the Pebble app receives the message, the data is passed in via a DictionaryIterator variable. You then use the dict_find() function to retrieve each Tuple.

Deploy the application, press the Select button on the Pebble, and observe the output printed in the Terminal application:

[INFO ] JS: helloJS: [JAVASCRIPT] Received message
from PEBBLE: {"key2":"A string","key1":35}
[INFO ] JS: helloJS: [JAVASCRIPT] ---key1: 35
[INFO ] JS: helloJS: [JAVASCRIPT] ---key2: A string
[INFO ] D helloJS.c:16 [PEBBLE] Message received
[INFO ] D helloJS.c:24 [PEBBLE] From JAVASCRIPT –
  key3: 25
[INFO ] D helloJS.c:29 [PEBBLE] From JAVASCRIPT –
  key4: Some string value
[INFO ] JS: helloJS: [JAVASCRIPT] Successfully
  delivered message with transactionId=0
[INFO ] D helloJS.c:7 [PEBBLE] Message delivered

The output demonstrates that the message sent by the JavaScript back to the Pebble is received successfully on the Pebble side.

Consuming a Web Service using JavaScript

The previous section touched on the communication pattern between the Pebble and the JavaScript. To make the example really useful, it would be good to make the JavaScript consume a Web service to demonstrate the usefulness of PebbleKit for JavaScript.

In this section, let’s modify the pebble-js-app.js file to make it consume a Yahoo Web service that retrieves the prices of some stock (see Listing 10).

You use the XMLHttpRequest class method to connect to the Web service and fetch its result. Once the result is obtained, you extract the stock prices and send them back to the Pebble.

In the helloJS.c file, add the highlighted code as shown in Listing 11.

Deploy the application and press the Select button on the Pebble once the application is running. After a while, you should see the stock prices on the Pebble (see Figure 22).

Figure 22 : The result returned by the Web service is shown on the Pebble.


In this article, I’ve taken you on a whirlwind tour of how to develop for the Pebble. You’ve learned how to create a simple Pebble application, build it, and then deploy it onto the Pebble. You’ve also learned how to:

  • Handle button clicks on the Pebble
  • Display the current time
  • Invert the screen
  • Draw shapes on the Pebble
  • Monitor for battery levels on the Pebble
  • Use PebbleKit for JavaScript to extend the functionalities of your Pebble app
  • Consume Web services using JavaScript

Hopefully, with this article you’ll now be able to create some interesting Pebble apps!