---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: The Road to Pebble SDK 3.0 in Ten Questions
author: thomas
tags:
- Freshly Baked
date: 2015-04-03
banner: /images/blog/road30-banner.png
---

We launched the first version of our new SDK with support for Pebble Time two
days into our Kickstarter campaign. Since then, we have updated it every week,
releasing six iterations of our developer preview. Some of these versions
included major new features like support for color in the emulator and support
for the timeline. Some other features were more subtle, like the new
antialiased drawing mode and the updates to the animation system. All these
changes together, and a bunch more that you have not seen yet, will form the
SDK that you will use to build watchfaces, watchapps and timeline apps for all
models of Pebble watches in the coming months.

These updates include lots of changes, and no matter how hard we try to
document everything there are a lot of unanswered questions. Some subjects have
just not been addressed by the developer previews yet, some need more
explaining and as always there are things we just missed in our communication
efforts.

In this update, I want to answer publicly the most frequent questions received
from the community in the last few weeks. I will cover components that have
been released but also those that are still to come so you have the information
needed to plan ahead and prepare your apps for the Pebble Time launch.

## What is the “timeline” shown in the Kickstarter video and how can I plug into it?

The timeline is a system-provided app that allows the user to navigate through
important events in their near future and their past.

![A glipse at the future (on the timeline) >{pebble-screenshot,pebble-screenshot--time-red}](/images/blog/road30-timeline.gif)

Apps have a very important role to play in the timeline. All Pebble apps (but
not companion apps that use our native PebbleSDK on Android and iOS) can push
“pins” to the timeline. From a developer perspective, pins are just a block of
JSON which describes the information to show to the user and what layout to use
to display the information (generic, calendar, sports or weather). Pins can
also include notifications that will be shown to the user when the pin is
created or updated on the watch, and reminders that can trigger at a specific
time. Finally pins can have actions attached to them. Actions can open a
Pebble app with a `uint32` parameter.

You can experiment with pins in CloudPebble’s new “Timeline” tab or with the
command line SDK and the `pebble insert-pin` command. For documentation, refer
to our [“Understanding Timeline Pins”](/guides/pebble-timeline/pin-structure/)
guide. Once you have nailed down what you want your pins to look and feel like
you can start pushing them via a public HTTP API. This API requires that your
app is submitted on the Pebble appstore; that is also how you get your API key
to use the timeline API. We have built a [Node.js
library](https://github.com/pebble/pebble-api-node) to make this easier and
David Moreau has already shared a
[PHP library](https://github.com/dav-m85/pebble-api-php/).

We also have a number of examples to help you get started:

 * A very simple [timeline hello
   world](https://github.com/pebble-examples/hello-timeline) that shows how
   to push a pin every time you press a button in your app
 * A [TV show tracker](https://github.com/pebble-examples/timeline-tv-tracker/)
   that announces your favorite tv show and lets users click on the pins to
   increase the number of viewers in realtime.

Get started now with our [Getting started with Timeline
post](/blog/2015/03/20/Getting-Started-With-Timeline/) or join us at the next
[Pebble SF developer meetup](http://www.meetup.com/PebbleSF/) on April 24th
where we will do a walkthrough of how to build a timeline app. If you are unable
to make it, you can also find this session on our [YouTube
channel](https://www.youtube.com/channel/UCFnawAsyEiux7oPWvGPJCJQ).

As a side note, many people have asked us what will happen if their app does
not integrate with the timeline. The new Pebble system still includes a main
menu in which all the apps will appear and they can be still be launched from
there.

## How can I build apps with transitions and animations like the ones shown in the Kickstarter videos?

The user interface in the new OS is full of extremely rich animations, and of
course we are not limiting them to the system applications. We want all apps to
take advantage of them and to achieve that, we are investing a lot of effort.

An essential part of all the animations is the core graphics framework used to
draw shapes, text and images. We have improved it a lot in recent weeks with
[much better support for animations](/guides/graphics-and-animations/animations),
[anti-aliasing and stroke width](/guides/graphics-and-animations/drawing-primitives-images-and-text/),
color bitmap handling and a lot of bugfixes and performance improvements.

Upcoming releases of the SDK will add the new UI components that you saw in the
Kickstarter video:

 * A new MenuLayer with support for color, infinite scrolling (i.e. looping back
   to the top), and new animations ![The new MenuLayer
   >{pebble-screenshot,pebble-screenshot--time-red}](/images/blog/road30-menulayer.gif)
 * A new ActionBar (available this week in developer preview 6!) that is wider,
   takes the full height of the screen, and includes some new animations.  ![The
   updated ActionBar
   >{pebble-screenshot,pebble-screenshot--time-red}](/images/blog/road30-actionbar.png)
 * An ActionMenuWindow which is a full screen component to select an action in a
   list. This is the same UI used to select an action on a pin or a
   notification, and it is available to all apps.  ![The new ActionMenuWindow
   >{pebble-screenshot,pebble-screenshot--time-red}](/images/blog/road30-actionmenu.gif)
 * A StatusBarLayer that gives you much more control over the status bar,
   including the ability to animate it and add goodies like a progress indicator
   and a card counter for the card pattern.  ![The new StatusBar
   >{pebble-screenshot,pebble-screenshot--time-red}](/images/blog/road30-statusbar.png)
 * Cards are not really a UI Component but more of a standard pattern, seen in
   the Weather app in the Kickstarter video and we expect that a lot more apps
   will want to re-use it because it is an extremely efficient way to represent
   information and looks very good! We will have a full example available to you
   very soon showing how to build this type of app.  ![A preview of the Weather
   app and its animations
   >{pebble-screenshot,pebble-screenshot--time-red}](/images/blog/road30-card.gif)

For more information on the design of apps in SDK 3.0, we are putting the
finishing touches on a completely new design guide. Stay tuned!

## What is up with the icons on Pebble Time? Can I use these effects in my applications?

A lot of the new design aesthetic is provided by icons, and those icons are not
your usual bitmap file. They animate during transitions to give a level of life
and personality never seen in any User Interface framework before.

They are vector based animations and we came up with a complete new set of APIs
called Pebble Drawing Commands, as well as a new file format (.pdc) to
implement them. Compared to bitmap icons they can be automatically animated by
the system depending on the context: the same icon can explode on the screen,
regroup into a dot or fly through one of the screen edges; all of this in a
fraction of the space it would normally take to do such effects with animated
PNGs.

We will be providing a large set of icons that you can use in your timeline
pins and in your apps. You will also be able to make your own animated icons to
play in your app.

## What is really happening when I compile an app today to be compatible with all Pebbles?

With the current version of the Pebble SDK, we compile your apps once with SDK
2.9 for Aplite (Pebble and Pebble Steel) and once with SDK 3.0 for Basalt
(Pebble Time and Pebble Time Steel). This means that your apps can have a
different look on each platform. This is going to be even more true as we
introduce the new UI components in the next few releases.

![Aplite differences](/images/blog/road30-aplite-differences.png)

However, as soon as we provide official support for Aplite in SDK 3.0, we will
start compiling against the same set of APIs and the UI components will have
similar sizes, look and behavior.

All the new software features of Pebble Time will be available on the original
Pebble, including the timeline, as well as the new UI components, PNG, Pebble
Drawing Commands, unlimited apps, AppFaces, etc. Of course features that are
hardware-dependent (such as the microphone) will not be supported on Aplite.

We encourage everyone to maintain support for both Aplite and Basalt in their
source code. You can easily do it with `#ifdef` calls. If you just want to
freeze your Aplite app as it is today and focus on your Basalt app this will be
possible too with a new feature in the build process coming in the near future.

## What are AppFaces and when can we use them?

AppFaces are previews of your app that are displayed in the launcher before the
user starts your app. To use them, your app will need to support being launched
without a UI to update the AppFace.

![AppFaces](/images/blog/road30-appfaces.png)

AppFaces will not be part of Pebble SDK when we launch Pebble Time. Your apps
will just appear with their name until we add this API at a later stage. This
also means that your app icons defined in the appinfo.json are not used with
the new launcher (but they will still appear on Aplite until 3.0 ships for
Aplite).

## When can we start interacting with smartstraps?

We have released a lot of [mechanical and electrical information about
smartstraps](/smartstraps/) but no APIs yet. You will quickly find out that
without software support, the smartstrap does not get any power.

We are working hard with our first smartstrap partners to finalize the
specifications for this API. If you want to be a part of this discussion,
[please make yourself known!](/contact)

## How can I get my hands on Pebble Time? When will users get them?

If you did not order one on Kickstarter you can register on [our
website]({{ site.links.pebble }}/pebble_time/) to be notified as soon as they are
available for normal orders. We will ship every Kickstarter pledge before we
start selling them on the Pebble website.  If you ordered one through the
Kickstarter campaign, we are working hard to get it to you as soon as possible.

As you know if you are a Kickstarter backer, Pebble Time starts shipping in May.

As a developer, you already have access to the emulator. It includes everything
you need to work on your app. If you are missing anything, [contact us](/contact).
With only about a month left, there is no time to lose!

## What about PebbleKit for iOS and Android? Any improvements?

We decided to focus all our efforts on the new UI framework and the timeline.
There are no functional changes to PebbleKit in SDK 3.0. However, all Android
apps must be recompiled with the new PebbleKit Android library to be compatible
with Pebble Time. If you do not recompile, your app will not work with Pebble
Time.

We have lots of plans for PebbleKit in the future and hope to share them
with you soon.

If you have suggestions for APIs you’d like to see, please
[contact us](/contact)

## Will developers have access to the microphone? What can we do with it?

Absolutely. We will give developers access to our speech-to-text APIs. You will
be able to show a UI to start a recording and your app will receive the text
spoken by the user. This API is coming soon™ - not in 3.0.

We are considering other APIs such as direct microphone access. Stay tuned for
more information on those.

## Can we submit apps for Pebble Time to the Pebble appstore now?

No, you should not. As long as you are using a developer preview SDK, the APIs
are non-final and apps built with the developer preview SDK may not work with
the final firmware and SDK shipped with Pebble Time.

Once the APIs are stable, we will announce a final SDK and encourage everyone
to push applications built with the final SDK to the Pebble appstore.

## Want more answers?

For more questions and answers, such as the future of the `InverterLayer`,
information on floating point support or whether Pebble is really powered by
unicorns and rainbows, please take a look at this [/r/pebbledevelopers
thread](http://www.reddit.com/r/pebbledevelopers/comments/314uvg/what_would_you_like_to_know_about_pebble_sdk_30/).

Much thanks to all the developers who contributed questions to prepare this
blog post!

If there is anything else you would like to know, [this
thread](http://www.reddit.com/r/pebbledevelopers/comments/314uvg/what_would_you_like_to_know_about_pebble_sdk_30/)
is just [one]({{site.links.forums_developer}}) [of
the](https://twitter.com/pebbledev) [many
ways](/contact) [to get in touch](http://www.meetup.com/pro/pebble/)
with us and get answers!


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Tips and Tricks - Transparent Images
author: chrislewis
tags:
   - Beautiful Code
---

Ever wondered how to draw transparent images in a Pebble app? This post will
walk you through the process.

In this post, we'll be using a sample image with a transparency component, shown
below:

When adding your project resource, ensure you set its ‘type’ correctly. On
CloudPebble, this is done when uploading the resource and choosing the 'PNG'
type. In the SDK, this is done with the `png` `type` in the project's
`appinfo.json`.

```js
"media": [
  {
    "type": "png",
    "name": "GLOBE",
    "file": "globe.png"
  }
]
```

This will create a resource ID to use in code:

```text
RESOURCE_ID_GLOBE
```

Simply create a ``GBitmap`` with this resource and draw the image with
``GCompOpSet`` as the compositing mode:

```c
static GBitmap *s_bitmap;
static Layer *s_canvas_layer;
```

```c
static void window_load(Window *window) {
  Layer *window_layer = window_get_root_layer(window);

  // Create GBitmap
  s_bitmap = gbitmap_create_with_resource(RESOURCE_ID_GLOBE);

  // Create canvas Layer
  s_canvas_layer = layer_create(layer_get_bounds(window_layer));
  layer_set_update_proc(s_canvas_layer, layer_update_proc);
  layer_add_child(window_layer, s_canvas_layer);
}
```

```c
static void layer_update_proc(Layer *layer, GContext *ctx) {
  // Draw the image with the correct compositing mode
  graphics_context_set_compositing_mode(ctx, GCompOpSet);
  graphics_draw_bitmap_in_rect(ctx, s_bitmap, gbitmap_get_bounds(s_bitmap));
}
```

When drawing on a ``TextLayer`` underneath `s_canvas_layer`, the result looks
like this:

![result-aplite >{pebble-screenshot,pebble-screenshot--steel-black}](/images/blog/tips-result-aplite.png)

See a full demo of this technique on the
[pebble-examples GitHub repo]({{site.links.examples_org}}/feature-image-transparent).

Job done! Any transparent pixels in the original image will be drawn as clear,
leaving the color beneath unaffected.

Read the [Image Resources](/guides/app-resources/) guide to learn more
about transparent PNGs.

## Conclusion

So there you have it. Using these examples you can easily implement transparency
on all Pebble platforms. To learn more, read the ``GCompOp`` documentation or
the
[`pebble-examples/feature-image-transparent`]({{site.links.examples_org}}/feature-image-transparent)
SDK example.

This is the latest version of the internal repository from Pebble Technology providing the software to run on Pebble watches. Proprietary source code has been removed from this repository and it will not compile as-is. This is for information only.


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Tips and Tricks - Platform-specific C File Set
author: chrislewis
tags:
   - Beautiful Code
---

In
[the last Tips and Tricks blog post](/blog/2015/05/13/tips-and-tricks-transparent-images/)
we looked at drawing transparent images on both the Aplite and Basalt platforms.

This time around, we will look at a `wscript` modification that can allow you to
build a Pebble project when you have two completely separate sets of C source
files; one set for Aplite, and another for Basalt.

Note: This technique can be applied only to local SDK projects, where access to
`wscript` is available. This means that it cannot be used in CloudPebble
projects.

> Update 05/20/15: There was an error in the JS code sample given at the end of
> this post, which has now been corrected.

## The Preprocessor Approach

In the [3.0 Migration Guide](/sdk/migration-guide/#backwards-compatibility) we
recommend using preprocessor directives such as `PBL_PLATFORM_APLITE` and
`PBL_PLATFORM_BASALT` to mark code to be compiled only on that particular
platform. This helps avoid the need to maintain two separate projects for one
app, which is especially convenient when migrating a 2.x app to the Basalt
platform.

```c
#ifdef PBL_PLATFORM_APLITE
  // Aligns under the status bar
  layer_set_frame(s_layer, GRect(0, 0, 144, 68));
#elif PBL_PLATFORM_BASALT
  // Preserve alignment to status bar on Aplite
  layer_set_frame(s_layer, GRect(0, STATUS_BAR_LAYER_HEIGHT, 144, 68));
#endif
```

This is a good solution for small blocks of conditional code, but some
developers may find that complicated conditional code can soon become more
`#ifdef...#elif...#endif` than actual code itself!

## The Modified Wscript Approach

In these situations you may find it preferable to use a different approach.
Instead of modifying your app code to use preprocessor statements whenever a
platform-specific value is needed, you can modify your project's `wscript` file
to limit each compilation pass to a certain folder of source files.

By default, you will probably have a project with this file structure:

```text
my_project
  resources
    images
      banner~bw.png
      banner~color.png
  src
    main.c
    util.h
    util.c
  appinfo.json
  wscript
```

In this scenario, the `wscript` dictates that *any* `.c` files found in `src`
will be compiled for both platforms.

To use a different set of source files for each platform during compilation,
modify the lines with the `**` wildcard (within the `for` loop) to point to a
folder within `src` where the platform- specific files are then located:

```python
for p in ctx.env.TARGET_PLATFORMS:
    ctx.set_env(ctx.all_envs[p])
    ctx.set_group(ctx.env.PLATFORM_NAME)
    app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)

    # MODIFY THIS LINE!
    # E.g.: When 'p' == 'aplite', look in 'src/aplite/'
    ctx.pbl_program(source=ctx.path.ant_glob('src/{}/**/*.c'.format(p)), target=app_elf)

    if build_worker:
        worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
        binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf})

        # MODIFY THIS LINE!
        # Also modify this line to look for platform-specific C files in `worker_src`
        ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/{}/**/*.c'.format(p)), target=worker_elf)

    else:
        binaries.append({'platform': p, 'app_elf': app_elf})
```

With this newly modified `wscript`, we must re-organise our `src` folder to
match the new search pattern. This allows us to maintain two separate sets of
source files, each free of any excessive `#ifdef` pollution.

```text
my_project
  resources
    images
      banner~bw.png
      banner~color.png
  src
    aplite
      main.c
      util.h
      util.c
    basalt
      main.c
      util.h
      util.c
  appinfo.json
  wscript
```

## Sharing Files Between Platforms

Using the modified wscript approach as shown above still requires any files that
are used on both platforms to be included twice: in the respective folder. You
may wish to reduce this clutter by moving any platform-agnostic files that both
platforms use to a `common` folder inside `src`.

You project might now look like this:

```text
my_project
  resources
    images
      banner~bw.png
      banner~color.png
  src
    aplite
      main.c
    basalt
      main.c
    common
      util.h
      util.c
  appinfo.json
  wscript
```

To tell the SDK to look in this extra folder during compilation of each
platform, further modify the two lines calling `ctx.pbl_program()` to include
the `common` folder in the array of paths passed to
[`ant_glob()`](https://waf.io/book/#_general_usage). This is shown in the code
snipped below, with unchanged lines ommitted for brevity:

```python
# Additionally modified to include the 'common' folder
ctx.pbl_program(source=ctx.path.ant_glob(['src/{}/**/*.c'.format(p), 'src/common/**/*.c']), target=app_elf)

/* Other code */

if build_worker:

    # Also modify this line to look for common files in '/worker_src/common/'
    ctx.pbl_worker(source=ctx.path.ant_glob(['worker_src/{}/**/*.c'.format(p), 'worker_src/common/**/*.c']), target=worker_elf)

else:

    /* Other code */

```

## Important Notes

While this new `wscript` allows us to keep our source files for each platform
separated entirely, there are a couple of important limitations to take into
account when using this method (without any further modification):

* There can still be only *one* JavaScript file, in `src/js/pebble-js-app.js`.
  You can simulate two JS files using a platform check:

```js
Pebble.addEventListener('ready', function() {
  if(Pebble.getActiveWatchInfo && Pebble.getActiveWatchInfo().platform === 'basalt') {
    // This is the Basalt platform
    console.log('PebbleKit JS ready on Basalt!');
  } else {
    // This is the Aplite platform
    console.log('PebbleKit JS ready on Aplite!');
  }
});
```

* Each binary can be bundled with only the app resources required for that
  specific platform. To learn how to package app resources with only a certain
  platform, read the
  [*Platform-specific Resources*](/guides/app-resources/platform-specific/)
  guide.

## Conclusion

With this modification to a Pebble project's `wscript`, developers now have two
options when it comes to diverging their app code for Aplite- and Basalt-
specific features **without** the need to maintain two completely separate
projects.

You can see a simple example project that ses all these techniques over at
[`pebble-examples/multi-platform-wscript`]({{site.links.examples_org}}/multi-platform-wscript).


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Migrating to Pebblekit iOS 3.0
author: alex
tags:
- Freshly Baked
---

Starting with Pebble Time Round, we are moving towards communicating with Bluetooth
Low-Energy only. This means there are some updates to PebbleKit iOS to support this. Here are
the major changes and steps to take to support the new BLE connection.

**What's New**

* Companion apps have dedicated, persistent communication channels
* Start mobile apps from Pebble
* 8K AppMessage buffers
* Swift support

## What do you need to do?
Import the new [PebbleKit iOS 3.0](/guides/migration/pebblekit-ios-3/#how-to-upgrade) library into your project.

### API Changes

#### NSUUIDs

You can now use `NSUUID` objects directly rather than passing ``appUUID`` as a `NSData` object.

**PebbleKit 2.x**

```c
uuid_t myAppUUIDbytes;
NSUUID *myAppUUID = [[NSUUID alloc] initWithUUIDString:@"226834ae-786e-4302-a52f-6e7efc9f990b"];
[myAppUUID getUUIDBytes:myAppUUIDbytes];
[PBPebbleCentral defaultCentral].appUUID = [NSData dataWithBytes:myAppUUIDbytes length:16];
```

**PebbleKit 3.0**

```c
NSUUID *myAppUUID = [[NSUUID alloc] initWithUUIDString:@"226834ae-786e-4302-a52f-6e7efc9f990b"];
[PBPebbleCentral defaultCentral].appUUID = myAppUUID;
```

#### Cold start PBPebbleCentral

You'll want to start ``PBPebbleCentral`` in a cold state now so users don't get
a pop-up asking for Bluetooth permissions as soon as the app initializes. Call
`[central run]` when it makes sense for the pop-up to show up. Add some custom
text to the dialog with `NSBluetoothPeripheralUsageDescription` to your
`Info.plist` file.

**PebbleKit 2.x**

 ```c
// MyAppDelegate.m - Set up PBPebbleCentral and run if the user has already
// performed onboarding
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [PBPebbleCentral defaultCentral].delegate = self;
  [PBPebbleCentral defaultCentral].appUUID = myAppUUID;
  if ([MySettings sharedSettings].userDidPerformOnboarding) {
    [[PBPebbleCentral defaultCentral] run];
  }
}
```

**PebbleKit 3.0**

```c
// MyOnboarding.m - Once the pop-up has been accepted, begin PBPebbleCentral
- (IBAction)didTapGrantBluetoothPermissionButton:(id)sender {
  [MySettings sharedSettings].userDidPerformOnboarding = YES;
  [[PBPebbleCentral defaultCentral] run]; // will trigger pop-up
}
```

### Specify that your app is built with PebbleKit 3.0 in the Developer Portal
Go to edit the companion app listing in your [developer portal](https://dev-portal.getpebble.com/developer) page and check the box for "Was this iOS app compiled with PebbleKit iOS 3.0 or newer?" This way, users on Pebble Time Round will be able to see your app in the appstore.

![](/images/blog/checkbox.png)

### Final thoughts

With a few quick steps, you can bring compatability for the BLE connection to your app. In the coming months, we'll be rolling out updates for users of Pebble and Pebble Time to take advantage of BLE-only connection as well. In the short term, if you intend to support Pebble Time Round, these steps are mandatory. For the complete details on migrating to PebbleKit 3.0, take a look at our [migration guide](/guides/migration/pebblekit-ios-3/). If you have any issues in migrating or have any questions concerning PebbleKit 3.0, feel free to [contact](/contact/) us anytime!


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Nuance Brings Pebble The Freedom Of Speech
author: jonb
tags:
- Freshly Baked
---

In October, we gave developers access to the microphone in the Pebble Time via
our new
[Dictation API](``Dictation``),
and almost instantly we began seeing awesome projects utilising speech input.
Voice recognition is an exciting new method of interaction for Pebble and it has
created an opportunity for developers to enhance their existing applications, or
create highly engaging new applications designed around the spoken word.

Speech-to-text has been integrated with Pebble by using the
[Recognizer](http://www.nuance.com/for-business/automatic-speech-recognition/automated-ivr/index.htm)
cloud service from Nuance, a leading provider of voice and language solutions.

## The Pebble Dictation Process

The Dictation API has been made incredibly easy for developers to integrate into
their watchapps. It’s also intuitive and simple for users to interact with.
Here’s an overview of the the dictation process:

1. The user begins by pressing a designated button or by triggering an event
   within the watchapp to indicate they want to start dictating. The watchapp’s
   UI should make this obvious.

2. The watchapp
   [initiates a dictation session](/docs/c/Foundation/Dictation/#dictation_session_start),
   assigning a
   [callback function](/docs/c/Foundation/Dictation/#DictationSessionStatusCallback)
   to handle the response from the system. This response will be either
   successful and return the dictated string, or fail with an error code.

3. The system Dictation UI appears and guides the user through recording their
   voice. The stages in the image below illustrate:

    a. The system prepares its buffers and checks connectivity with the cloud
       service.

    b. The system begins listening for speech and automatically stops listening
       when the user finishes talking.

    c. The audio is compressed and sent to the cloud service via the mobile
       application.

    d. The audio is transcribed by the cloud service and the transcribed text is
       returned and displayed for the user to accept or reject (this behaviour
       can be
       [programmatically overridden](/docs/c/Foundation/Dictation/#dictation_session_enable_confirmation)).

4. Once the process has completed, the registered callback method is fired and
   the watchapp can deal with the response.

![dictation-flow](/images/blog/dictation-flow.png)

## But How Does It Actually Work?

Let’s take a closer look at what’s happening behind the scenes to see what’s
really going on.

![dictation-recognizer](/images/blog/dictation-recognizer.png)

1. To capture audio, Pebble Time (including Time Steel and Time Round) has a
   single [MEMS](https://en.wikipedia.org/wiki/Microelectromechanical_systems)
   microphone. This device produces output at 1 MHz in a
   [PDM](https://en.wikipedia.org/wiki/Pulse-density_modulation) format.

2. This 1 bit PDM signal needs to be converted into 16-bit
   [PCM](https://en.wikipedia.org/wiki/Pulse-code_modulation) data at 16 kHz
   before it can be compressed.

3. Compression is performed using the [Speex](http://www.speex.org/) encoder,
   which was specifically designed for speech compression. Compression needs to
   occur in order to reduce the overall size of the data before it’s transferred
   via bluetooth to the mobile application. Speex also has some additional
   advantages like tuneable quality/compression and recovery from dropped
   frames.

4. The mobile application sends the compressed data to Nuance Recognizer, along
   with some additional information like the user’s selected language.

5. Nuance performs its magic and returns the textual representation of the
   spoken phrase to the mobile application, which is then automatically passed
   back to the watchapp.

6. The Dictation UI presents the transcribed text back to the user where they
   can choose to accept or reject it.

## About the Dictation API

Behind the scenes there’s a lot going on, but let’s take a look at how minimal
the code needs to be in order to use the API.

1. Create a static variable as a reference to the dictation session:

    ```c
    static DictationSession *s_dictation_session;
    ```

2. Create a callback function to receive the dictation response:

    ```c
    static void dictation_session_callback(DictationSession *session, DictationSessionStatus status, char *transcription, void *context) {
      if(status == DictationSessionStatusSuccess) {
        APP_LOG(APP_LOG_LEVEL_DEBUG, "Transcription:\n\n%s", transcription);
      } else {
        APP_LOG(APP_LOG_LEVEL_DEBUG, "Transcription failed.\n\nError ID:\n%d", (int)status);
      }
    }
    ```

3. Within a button click or other event, create a ``DictationSession`` to begin
   the process:

    ```c
    s_dictation_session = dictation_session_create(512, dictation_session_callback, NULL);
    ```

4. Before your app exits, don’t forget to destroy the session:

    ```c
    dictation_session_destroy(s_dictation_session);
    ```

## Voice-enabled Watchapps

Here’s a small sample of some of the watchapps which are already available in
the Pebble appstore which utilise the Dictation API.

* [Voice2Timeline](http://apps.getpebble.com/en_US/application/561f9188bcb7ac903a00005b)
  is a handy tool for quickly creating pins on your timeline by using your
  voice. It already works in 6 different languages. You can leave notes in the
  past, or even create reminders for the future (e.g. “Don’t forget the milk in
  1 hour”).

* [Translate (Vox Populi)](http://apps.getpebble.com/en_US/application/561ff3cbbcb7aca6250000a3)
  allows a user to translate short phrases and words into a different language.
  It uses the [Yandex](https://translate.yandex.com/) machine translator API
  which supports more than 60 different languages.

* [Checklist](http://apps.getpebble.com/en_US/application/5620e876768e7ada4e00007a)
  is a really simple tool which generates a list of items using your voice. It
  even allows you to enter multiple items at once, by specifying a comma or
  period. You can easily mark them as completed by pressing the ‘select’ button
  on each item.

* [Smartwatch Pro](https://itunes.apple.com/gb/app/smartwatch-pro-for-pebble/id673907094?mt=8)
  (iOS) has been updated to give users voice controlled music playback, create
  reminders and even create tweets by using their voice.

## Final Thoughts

Why not also checkout this video of Andrew Stapleton (Embedded Developer) as he
deep dives into the internals of the Pebble Dictation API during his presentation
at the [Pebble Developer Retreat 2015](/community/events/developer-retreat-2015/).

[EMBED](www.youtube.com/embed/D-8Ng24RXwo)

We hope you’ve seen how flexible and easy it is to use the new Dictation API,
and perhaps it will inspire you to integrate voice into your own application or
watchface - if you create a voice enabled watchapp, let us know by tweeting
[@pebbledev](https://twitter.com/pebbledev).

If you’re looking to find out more about voice integration, checkout our
[developer guide](/guides/events-and-services/dictation/),
[API documentation](``Dictation``)
and our
[simple example app](https://github.com/pebble-examples/simple-voice-demo). We
also have a very friendly and helpful Pebble community on Discord; why not
[join us]({{ site.links.discord_invite }})?


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Introducing Pebble Tool 4.0
author: katharine
tags:
- Freshly Baked
---

I am pleased to today announce that version 4.0-rc4 of the `pebble` tool is now
available. The key new feature is a new paradigm for dealing with firmware and
SDK versions. This makes it much easier to deal with differing SDK versions, or
to test code on multiple (emulated) firmware versions.

_A note: while the tool is now at version 4.0, the SDK, firmware and mobile apps
will not be following. Pebble tool versioning is now completely independent of
the rest of the Pebble ecosystem._

Managing SDKs
------------

The pebble tool now manages SDKs for you, without you needing to download and
install the entire SDK manually each time. The first time you need an SDK, the
latest one will be automatically installed for you. After that, you can use the
SDK operations that live under the `pebble sdk` subcommand.

To see a list of available SDKs, use `pebble sdk list`:

```nc|text
katharine@kbrmbp ~> pebble sdk list
Installed SDKs:
3.7 (active)

Available SDKs:
3.6.2
3.4
3.3
3.2.1
3.1
3.0
2.9
```

You can install any SDK using `pebble sdk install`, like so:

```nc|text
katharine@kbrmbp ~> pebble sdk install 3.6.2
Installing SDK...
Do you accept the Pebble Terms of Use and the Pebble Developer License? (y/n) y
Downloading...
100%[======================================================]   1.40 MB/s 0:00:01
Extracting...
Preparing virtualenv... (this may take a while)
Installing dependencies...
Done.
Installed.
```

You can switch between active SDKs using `pebble sdk activate <version>`, like
`pebble sdk activate 3.7`. Once you activate an SDK, it will be used for all
`build` and `install` commands.

Switching on the fly
--------------------

A number of commands now take an optional `--sdk` flag, which will override the
current active SDK. This enables you to easily run one command with a different
SDK version — for instance, compiling with 3.6.2 and then running on 3.7:

```nc|text
katharine@kbrmbp ~> pebble build --sdk 3.6.2
# ...
katharine@kbrmbp ~> pebble install --emulator basalt --sdk 3.7
# ...
```

This is supported by `pebble build` as well as any command that supports
`--emulator`. Additionally, you can now run emulators for multiple SDKs
simultaneously by passing different values for `--sdk`.

Benefits
--------

Beyond the obvious benefit of easier SDK management, the new system also
produces much smaller SDKs. Each SDK used to be a 38 MB download, which
decompressed to 143 MB, plus another hundred megabytes for the toolchain.
Most of this is now downloaded only once, as part of the initial pebble tool
setup. After that, each SDK is only a 2 MB download, which expands to 4 MB on
disk.

The new pebble tool can also alert you to new SDKs as they become available,
enabling you to install them with a single command.

Try it out!
-----------

To try out our new pebble tool, read the instructions on the [SDK
Beta](/sdk/beta) page.

Please [contact us](/contact/) if you run into any issues installing or using
`pebble` v4.0, or if you have any feedback. You can also frequently find me
on ~~Slack~~ Discord — [join us]({{ site.links.discord_invite }})!


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Unifying bitmap resources
author: katharine
tags:
- Freshly Baked
---

With the upcoming release of firmware 3.8 on Pebble and Pebble Steel, and the
associated SDK 3.8, we have decided to redesign how image resources work in
Pebble apps.

Why change?
-----------

First, some history: when SDK 1 was originally released, and continuing up to
SDK 2.9, there was a single image type, `png`. A `png` resource would take a PNG
as input and spit out a custom, uncompressed, 1-bit-per-pixel image format we
called "pbi". This was the only bitmap format Pebble supported, and life was
simple.

With the release of SDK 3.0, we added firmware support for a new image format:
PNG (with some restrictions). This enabled Pebble to directly read compressed
images, and those images could be 1-bit, 2-bit, 4-bit or 8-bit palettised. The
existing `png` resource type was changed to produce these images instead of the
old PBI format, and everyone had smaller image resources.

Unfortunately, "png" isn't the best option for all cases. The old 1-bit format
supported some [compositing operations](``GCompOp``) that other image formats
do not support. We added the `pbi` format to achieve this legacy behavior.
Additionally, PNG decompression isn't free: loading a PNG resource requires
enough memory to hold the compressed image, the uncompressed image, and some
scratch space. This was often offset by the benefits of palettised images with
fewer bits per pixel, but sometimes it didn't fit. We added `pbi8`, which
produced 8-bit-per-pixel PBI images. This still left it impossible to generate
a palettized PBI, even though the format does exist.

As a further complication, since SDK 1 and continuing through the present day,
`pbi` (and `pbi8`) images have cropped transparent borders around the outside.
However, `png` images (as of SDK 3) do _not_ crop like this. The cropping
behavior was originally a bug, and is generally undesirable, but must be
maintained for backwards compatibility.

There is one additional exception to all of this: until SDK 3.8, the Aplite platform still
interprets `png` to mean `pbi`. It also interprets `pbi8` to mean `pbi`. When
we built the 3.8 SDK, we changed `png` to really mean `png` on Aplite.
Unfortunately, the more limited memory of the Aplite platform meant that these
PNGs sometimes did not have enough space to decompress. The only workaround was
to duplicate resources and use `targetPlatforms` to specify a `pbi` resource for
Aplite and a `png` resource for Basalt and Chalk.

The easiest answer was to keep `png` as an alias for `pbi` on Aplite—but then
there's no way of generating a real PNG for Aplite. Furthermore, the `png`,
`pbi` and `pbi8` trio was getting confusing, so we decided to do something else.

"bitmap" to the rescue
----------------------

As of SDK 3.8, `png`, `pbi` and `pbi8` **are all deprecated**. We are instead
introducing a new resource type, `bitmap`. This new resource type unifies all
the existing types, allows the SDK to use its best judgement, increases the
flexibility available to developers, and takes the guesswork out of advanced
image manipulation. It also removes the generally undesirable forced cropping
behavior.

The simplest option is to only specify that you want a `bitmap` resource, and
by default the SDK will do the most reasonable thing:

```js
{
	"type": "bitmap",
	"name": "IMAGE_BERRY_PUNCH",
	"file": "images/berry-punch.png"
}
```

This will create an image with the smallest possible in-memory representation,
which will depend on the number of colors. On Aplite, where memory is tight,
it will optimize for low memory consumption by creating a pbi. On all other
platforms, where there is more memory to spare, it will create a png.

This behavior can be overridden using the following attributes:

* `memoryFormat`: This determines the `GBitmapFormat` that the resulting image
  will have. The default value is `Smallest`, which picks the value that will
  use the least memory. If you have specific requirements, you can specify:
  `SmallestPalette`, `1BitPalette`, `2BitPalette`, `4BitPalette`, `1Bit` or
  `8Bit`. If an image cannot be represented in the requested format, a build
  error will result. This, for instance, enables palette manipulation with
  static checking for confidence that you will have an appropriate palette to
  manipulate.
* `spaceOptimization`: This determines whether we should optimize the image for
  resource space (`storage`) or memory (`memory`). The default depends on the
  memory available on the platform: Aplite defaults to `memory`, while other
  platforms default to `storage`.
* `storageFormat`: This explicitly states whether an image should be stored on
  flash as a PNG or pbi image. In most cases you should not specify this, and
  instead depend on `spaceOptimization`. However, if you read resources
  directly, this option exists to force a value.

So if you want an image that will always have a 2-bit palette and use as little
memory as possible, you can do this:

```js
{
	"type": "bitmap",
	"name": "IMAGE_TIME_TURNER",
	"file": "images/time-turner.png",
	"memoryFormat": "2BitPalette",
	"spaceOptimization": "memory"
}
```

The resulting image will always have `GBitmapFormat2BitPalette`, even if it
could have a 1-bit palette. If it has more than four colors, the build will
fail.

In SDK 3.8-beta8, this would result in a 2-bit palettized pbi. However, this
is not guaranteed: we can change the format in the future, as long as the result
has ``GBitmapFormat2BitPalette`` and we prefer to optimise for memory
consumption where possible.

Finally, note that some combinations are impossible: for instance, a `1Bit`
image can never be stored as a PNG, so `"storageFormat": "png"` combined with
`"memoryFormat": "1Bit"` will be a compile error.

Migration
---------

If you have an existing app, how do you migrate it to use the new `bitmap` type?

If you were depending on the `pbi` cropping behavior, you will have to manually
crop your image. Beyond that, this table gives the equivalences:

| SDK 3.7 type | `bitmap` specification                       |
|--------------|----------------------------------------------|
| `png`        | `{"type": "bitmap"}`                         |
| `pbi`        | `{"type": "bitmap", "memoryFormat": "1Bit"}` |
| `pbi8`       | `{"type": "bitmap", "storageFormat": "pbi"}` |

`png-trans` has been left out of this entire exercise. Its behavior is unchanged:
it will produce two pbis with `GBitmapFormat1Bit`. However, `png-trans` is also
deprecated and discouraged. As of SDK 3.8, all platforms support transparency
in images, and so should use `bitmap` instead.

If you have any questions, you can [find us on Discord]({{ site.links.discord_invite }})
or [contact us](/contact/).


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Bringing the Family Back Together - 3.x on Aplite is Almost Here!
author: chrislewis
tags:
- Freshly Baked
banner: /images/blog/3.x-on-tintin.png
---

The time has almost come for Pebble Classic and Pebble Steel to get firmware
3.x!

This is great news for users who get access to all the improvements and fixes
since 2.x, as well as new features such as timeline, Quiet Time, no more eight
app limit, Standby Mode, and last but not least one new color (gray)!

But let us not forget about Basalt and Chalk app developers, who also have a lot
to get excited about - a huge new user base of Aplite watch wearers!

This blog post aims to help developers using SDK 2.x features and APIs to
migrate their apps to a single 3.x codebase. As well as bringing compatibility
to all older watches when they upgrade to firmware 3.x, these changes will also
serve to remove a lot of complex and ugly conditional code that has needed to
exist in the meantime. Let's fix some apps!

## Get the Beta SDK

To try out the beta SDK, read the instructions on the [SDK Beta](/sdk/beta)
page.

## Mandatory Changes

To be compatible with users who update their Pebble Classic or Pebble Steel to
firmware 3.x the following important changes **MUST** be made:

* If you are adding support for Aplite, add `aplite` to your `targetPlatforms`
  array in `appinfo.json`, or tick the 'Build Aplite' box in 'Settings' on
  CloudPebble.

* Recompile your app with at least Pebble SDK 3.8 (coming soon!). The 3.x on
  Aplite files will reside in `/aplite/` instead of the `.pbw` root folder.
  Frankenpbws are **not** encouraged - a 2.x compatible release can be uploaded
  separately (see [*Appstore Changes*](#appstore-changes)).

* Update any old practices such as direct struct member access. An example is
  shown below:

    ```c
    // 2.x - don't do this!
    GRect bitmap_bounds = s_bitmap->bounds;

    // 3.x - please do this!
    GRect bitmap_bounds = gbitmap_get_bounds(s_bitmap);
    ```

* If your app uses either the ``Dictation`` or ``Smartstrap`` APIs, you must
  check that any code dependent on these hardware features fails gracefully when
  they are not available. This should be done by checking for `NULL` or
  appropriate `enum` values returned from affected API calls. An example is
  shown below:

    ```c
    if(smartstrap_subscribe(handlers) != SmartstrapResultNotPresent) {
      // OK to use Smartstrap API!
    } else {
      // Not available, handle gracefully
      text_layer_set_text(s_text_layer, "Smartstrap not available!");
    }

    DictationSession *session = dictation_session_create(size, callback, context);
    if(session) {
      // OK to use Dictation API!
    } else {
      // Not available, handle gracefully
      text_layer_set_text(s_text_layer, "Dictation not available!");
    }
    ```

## Appstore Changes

To handle the transition as users update their Aplite to firmware 3.x (or choose
not to), the appstore will include the following changes:

* You can now have multiple published releases. When you publish a new release,
  it doesn’t unpublish the previous one. You can still manually unpublish
  releases whenever they want.

* The appstore will provide the most recently compatible release of an app to
  users. This means that if you publish a new release that has 3.x Aplite
  support, the newest published release that supports 2.x Aplite will be
  provided to users on 2.x Aplite.

* There will be a fourth Asset Collection type that you can create: Legacy
  Aplite. Apps that have different UI designs between 2.x and 3.x on Aplite
  should use the Legacy Aplite asset collection for their 2.x assets.

## Suggested Changes

To fully migrate to SDK 3.x, we also suggest you make these non-essential
changes:

* Remove any code conditionally compiled with `PBL_SDK_2` defines. It will no
  longer be compiled at all.

* Ensure that any use of ``app_message_inbox_size_maximum()`` and
  ``app_message_outbox_size_maximum()`` does not cause your app to run out of
  memory. These calls now create ``AppMessage`` buffers of 8k size by default.
  Aplite apps limited to 24k of RAM will quickly run out if they use much more
  memory.

* Colors not available on the black and white Aplite display will be silently
  displayed as the closet match (black, gray, or white). We recommend checking
  every instance of a `GColor`to ensure each is the correct one.

* In addition to the point above, investigate how the contrast and readability
  of your app can be improved by making use of gray (either `GColorLightGray` or
  `GColorDarkGray`). Examples of this can be seen in the system UI in the banner
  at the top of this blog post.

* Apps using image resources should take advantage of the new `bitmap` resource
  type, which optimizes image files for you. Read the
  [*Unifying Bitmap Resources*](/blog/2015/12/02/Bitmap-Resources/)
  blog post to learn more.

## Questions?

That's it! It will be quite straightforward to update most of your apps, but if
you do have any problems or queries, feel free to
[contact us](/contact/) or find us on [Discord]({{ site.links.discord_invite }}).


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Multiple JavaScript Files
author: katharine
tags:
- Freshly Baked
---

In SDK 3.9 we will introduce a new feature to the Pebble SDK: the ability to
cleanly use multiple JavaScript files in the PebbleKit JS app portion of
your project.

<strike>SDK 3.9 is available now in beta! Check out our
[beta instructions](/sdk/download/#testing-beta-sdks) to try it out.</strike>

**EDIT:** SDK 3.9 is now [publicly available](/sdk/) - to update your SDK, run: ```pebble sdk install latest```

# How do I use this?

First, if you are using the native SDK, you must make sure you have
`"enableMultiJS": true` in your appinfo.json file. This defaults to false if
omitted, but will be set to `true` in all projects created using SDK 3.9+ and
Pebble Tool 4.1+. If you are using CloudPebble, ensure that "JS Handling" is
set to "CommonJS-style" in your project's settings page.

Having enabled it, PebbleKit JS now expects to have an "entry point" at
`src/pkjs/index.js`. This is the code we will run when your app starts, and is
equivalent to the native SDK's old `src/js/pebble-js-app.js`. Aside from a
slightly less redundant name, the effective behavior is exactly the same as
before.

However, you can now add additional javascript files! These files will not be
executed automatically. Instead, you can pull them in to your code using the
`require` function, which returns a reference to the imported file.

Since `require` returns something, there needs to be some way to provide
something that it can usefully return. To do this, add properties to
`module.exports` in the file to be required. For instance:

**adder.js**

```js
function addThings(a, b) {
  return a + b;
}

module.exports.addThings = addThings;
```

**index.js**

```js
var adder = require('./adder');
console.log("2 plus 2 is " + adder.addThings(2, 2));
```

Running the app with this JavaScript would look like this:

```nc|text
katharine@scootaloo ~> pebble install --emulator basalt --logs
Installing app...
App install succeeded.
[17:39:40] javascript> 2 plus 2 is 4
```

That's about all there is to it: we handle this all transparently.

# But…!?

### …how do I migrate my existing project?

If you use the native SDK and your existing project has only one JavaScript
file, just move it from `src/js/pebble-js-app.js` to `src/pkjs/index.js` and add
`"enableMultiJS": true` to your appinfo.json file. Easy!

If you use CloudPebble and have multiple JavaScript files, you first change
"JS Handling" to "CommonJS-style" in your project's Settings page. If you don't
have a JavaScript file called "index.js", but you do have some others, it will
prompt you to rename a file. If you previously used multiple JavaScript files
on CloudPebble, you will now need to make code changes. In particular,
you will have to modify your code so that there is a single, clearly-defined
entry point (`index.js`) and the other files use the module export system
described above.

That said, there is no need to do this now: the existing system will continue to
work for the forseeable future.

### …will this work for users who haven't updated their mobile apps?

Yes! This all happens entirely at build time; no updates to the user's phone
software are required. Since it is part of SDK 3.9, they will need to be running
firmware 3.9, as usual.

### …I use Pebble.js. What does this mean for me?

Nothing! Pebble.js already does something very similar to this, and it will
continue to do so. No changes are needed.

### …I built something like this myself. Do I have to use this?

No! If you want to keep using your own custom system, you can set
`enableMultiJS` to false or omit it entirely, and nothing will change. The
system is strictly opt-in, and won't break anything you were doing if you
leave it as-is.

### …I don't want my main JavaScript file to be called "index.js"

We recommend that you use the default name for portability reasons. In
particular, CloudPebble will _require_ that you use this name, and will fail
to build a non-compliant project. However, if you would really like to change
it, you can pass an alternate path as `js_entry_file` to `pbl_bundle` in your
wscript file.


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: JavaScript Libraries for C Developers (pt. 1)
author: cat
tags:
- Freshly Baked
- Beautiful Code

---

One of the exciting changes introduced in [SDK 3.9] [sdk 3.9] was support for
including [Multiple JavaScript Files] [js blog] to your projects. While this
feature doesn’t allow you to run any JavaScript you previously couldn’t, it
makes organizing your PebbleKit JS code a heck of a lot easier.

In this blog post, we'll look at how to refactor some exisiting JavaScript
'library code' into an actual module for PebbleKit JS, making it simpler to use
and share!

## OWM-Weather

A few months ago we published a very simple 'library' for working with the
[Open Weather Map] [owm] API. The JavaScript side of the code takes care of
grabbing the user’s location with the built in [geolocation.getCurrentPosition]
[getcurrentposition] API, then fetches weather information for the returned
position.

The initial version of the code we’re working with can be found [here] [owm lib 0].

## Creating JavaScript Classes

> **Note:** While JavaScript (with ECMAS5) does not allow for true "Classes",
> JavaScript does a support a mechanism that allows you write Class-like code.
> For convience, we'll be refering to these objects as 'classes' throughout
> this post.

The first thing we want to do is wrap our library code in an Object constructor
function, which is what allows us to treat `OWMWeather` as a class, and rewrite
our functions so they're properties belonging to `this` (an instantiated
version of the object). If you're interested in diving a bit deeper into how
this works, take a look at Pivotal's great blog post about [JavaScript
constructors, prototypes, and the 'new' keyword] [pivotal blog].

```js
var OWMWeather = function() {
  this.owmWeatherAPIKey = '';

  this.owmWeatherXHR = function(url, type, callback) {
    //...
  };
  //...
};
```

We’ll also need to change our JS application code to construct a new
`OWMWeather` object, and change our reference of `appMessageHandler()` in the
in the `Pebble.addEventListener('appmessage', ...)` callback to
`owmWeather.appMessageHandler()`.

```js
var owmWeather = new OWMWeather();

Pebble.addEventListener('ready', function(e) {
  console.log('PebbleKit JS ready!');
});

Pebble.addEventListener('appmessage', function(e) {
  console.log('appmessage: ' + JSON.stringify(e.payload));
  owmWeather.appMessageHandler(e);
});
```

## Using the .bind() function

If we try to run our code at this point in time, we're going to run into a slew
of errors because we've changed the scope of the functions, but we haven't
updated the places where we call them. We need to change
`owmWeatherFunctionName()` to `this.owmWeatherFunctionName()` (same goes for
the API key). Here's what the new `owmWeatherLocationSuccess` method looks like
(changes are indicated with in-line comments):

```js
this.owmWeatherLocationSuccess = function(pos) {
  // Change owmWeatherAPIKey to this.owmWeatherAPIKey
  var url = 'http://api.openweathermap.org/data/2.5/weather?' +
    'lat=' + pos.coords.latitude + '&lon=' + pos.coords.longitude +
    '&appid=' + this.owmWeatherAPIKey;
  console.log('owm-weather: Location success. Contacting OpenWeatherMap.org..');

  this.owmWeatherXHR(url, 'GET', function(responseText) {
    console.log('owm-weather: Got API response!');
    if(responseText.length > 100) {
      // Change owmWeatherSendToPebble(..) to this.owmWeatherSendToPebble(..)
      this.owmWeatherSendToPebble(JSON.parse(responseText));
    } else {
      console.log('owm-weather: API response was bad. Wrong API key?');
      Pebble.sendAppMessage({ 'OWMWeatherAppMessageKeyBadKey': 1 });
    }
  // Add .bind(this) to the closing brace of the callback
  }.bind(this));
};
```

An observant developer might notice that along with changing `owmWeatherXHR` to
`this.owmWeatherXHR`, we've also added `.bind(this)` to the end of the callback
function.

We're not going to dive too deeply into how `this` and `bind` works (that's a
whole blog post on its own), but what I will say is that the `bind` method can
be thought of as modifying a function so that it will, when invoked, have its
`this` keyword set to the provided parameter.

> If you want to learn more about JavaScript Objects, scope, and `bind`, I will
> encourage you to read [You Don't Know JS: *this* & Object Prototypes] [js book].

We'll want use `bind(this)` (where `this` will be the instance of the
OWMWeather class) whenever we're using an OWMWeather method as a callback from
within the OWMWeather code.

```js
navigator.geolocation.getCurrentPosition(
  this.owmWeatherLocationSuccess.bind(this),
  this.owmWeatherLocationError.bind(this), {
    timeout: 15000,
    maximumAge: 60000
});
```

At this point, our code should look like [this] [owm lib 1].

## Using module.exports

The last thing we want to do to make this into a handy module is extract the
code into its own file (`/src/js/lib/owm_weather.js`), and use the
[module.exports] [module exports] API to export our OWMWeather class.

```js
var OWMWeather = new function() {
  //...
};

module.exports = OWMWeather;
```

In order to use this in our PebbleKit JS application, we need to do a couple things..

If you're using CloudPebble:

- Change **JS Handling** to _CommonJS-style_ in the project settings

If you're using the SDK:

- Update our `appinfo.json` to include `'enableMultiJS': true` if it isn't
  already there

- Rename `src/js/pebble-js-app.js` to `src/js/app.js`

Once we've made these changes to our files, we're ready to include OWMWeather
in our `app.js` file with the [require API] [require api].

```js
var OWMWeather = require('./lib/owm_weather.js');
var owmWeather = new OWMWeather();

Pebble.addEventListener('ready', function(e) {
  console.log('PebbleKit JS ready!');
});

Pebble.addEventListener('appmessage', function(e) {
  console.log('appmessage: ' + JSON.stringify(e.payload));
  owmWeather.appMessageHandler(e);
});
```

At this point, our code should something look like [this][owm lib 2].

## Refactoring Variable and Function Names

Since we’ve moved all of the OWMWeather code into a class, we can safely remove
the `owmWeather` prefixes on all of our methods and properties. While we’re at
it, we're also going to rename functions and properties that are intended to be
private to begin with an `_`, which is fairly common practice:

```js
var OWMWeather = function() {
  this._apiKey = '';

  this._xhrWrapper = function(url, type, callback) {
    //...
  };
  //...
};
```

## What's next..

And that's it - we've successfuly refactored our code into a module that should
be easier for developers to use and share (which is exactly what we want). You
can view the full source code for this blog post [here][owm lib final].

In the next blog post, we'll take this library a step further and look at how
you can abstract away the need for `appKeys` in your `appinfo.json` file to
make working with the library *even easier*...

Until then, happy hacking!

[sdk 3.9]: /sdk/changelogs/3.9/
[js blog]: /blog/2016/01/29/Multiple-JavaScript-Files/

[owm]: http://openweathermap.org/

[getcurrentposition]: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition

[pivotal blog]: https://blog.pivotal.io/labs/labs/javascript-constructors-prototypes-and-the-new-keyword

[js book]: https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes

[module exports]: http://www.sitepoint.com/understanding-module-exports-exports-node-js/

[require api]: http://www.sitepoint.com/understanding-module-exports-exports-node-js/#importing-a-module

[owm lib 0]: https://github.com/pebble-hacks/owm-weather/tree/8c4f770e591fe5eff65209ebb6fe6ef23152d81a

[owm lib 1]: https://github.com/pebble-hacks/owm-weather/blob/8c9ab77a66d5c38719acbdfce939fbfac6d12235/owm_weather/owm_weather.js

[owm lib 2]: https://github.com/pebble-hacks/owm-weather/tree/597c717627c281b56ff303ca94f35789002a969e

[owm lib final]: https://github.com/pebble-hacks/owm-weather/tree/657da669c3d9309a956f655c65263b8dc06cec1f


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Introducing App Debugging
author: katharine
tags:
- Freshly Baked
---

Happy leap day! Today is a once-every-four-years day of bizarre date-related
bugs, and thus an opportune moment for us to introduce our latest developer
feature: app debugging in the emulator! This gives you a powerful new way to
hunt down errors in your apps.

A new command is available in our preview release of pebble tool 4.2 and
SDK 3.10: `pebble gdb`.
Running this command from a project directory on an emulator with your app
installed will pause the emulator and attach a gdb debugger instance
to it:

```nc|text
katharine@scootaloo ~/p/pebble-caltrain (master)> pebble gdb --emulator basalt
Reading symbols from /Users/katharine/Library/Application Support/Pebble SDK/SDKs/3.10-beta4/sdk-core/pebble/basalt/qemu/basalt_sdk_debug.elf...(no debugging symbols found)...done.
Remote debugging using :49229
0x0801cd8c in ?? ()
add symbol table from file "/Users/katharine/projects/pebble-caltrain/build/basalt/pebble-app.elf" at
	.text_addr = 0x200200a8
	.data_addr = 0x20023968
	.bss_addr = 0x200239b8
Reading symbols from /Users/katharine/projects/pebble-caltrain/build/basalt/pebble-app.elf...done.
Breakpoint 1 at 0x804aebc

Press ctrl-D or type 'quit' to exit.
Try `pebble gdb --help` for a short cheat sheet.
Note that the emulator does not yet crash on memory access violations.
(gdb)
```

Do note that once we pause execution by launching gdb, emulator buttons and
any pebble tool command that interacts with the emulator won't work until we
continue execution (using `continue` or `c`) or quit gdb.

Once we're here, we can add some breakpoints to the app in interesting places.
Here we are debugging my
[Caltrain app](https://github.com/Katharine/pebble-caltrain). Let's say I think
there's a bug in the search for the next train: I probably want to poke around
in [`find_next_train`](https://github.com/Katharine/pebble-caltrain/blob/f4983c748429127a8af85911cb123bd8c3bacb73/src/planning.c#L4).
We can run `b find_next_train` to add a breakpoint there:

```nc|text
(gdb) b find_next_train
Breakpoint 2 at 0x20020f1e: file ../src/planning.c, line 5.
(gdb)
```

Now we can use the `c` or `continue` command to set my app running again, until
it stops at `find_next_train`:

```nc|text
(gdb) c
Continuing.
```

The app runs as usual until we open a station, which causes it to look up a
train, where it hits the breakpoint and pauses the app so we can inspect it:

```nc|text
Breakpoint 2, find_next_train (count=184, times=0x2002f414, nb=0x2001873c,
    sb=0x20018738) at ../src/planning.c:5
5	  TrainTime *best[2] = {NULL, NULL};
(gdb)
```

Now we can see how we got here using the `backtrace` or `bt` command:

```nc|text
(gdb) bt
#0  find_next_train (count=184, times=0x2002f414, nb=0x2001873c, sb=0x20018738)
    at ../src/planning.c:7
#1  0x200211b2 in next_train_at_station (station=13 '\r',
    northbound=0x20025a0c <s_northbound>, southbound=0x20025a14 <s_southbound>)
    at ../src/planning.c:76
#2  0x200215c8 in prv_update_times () at ../src/stop_info.c:106
#3  0x200216f8 in show_stop_info (stop_id=13 '\r') at ../src/stop_info.c:174
#4  0x200219f0 in prv_handle_menu_click (menu_layer=0x2002fe3c,
    cell_index=0x2002ff0c, context=0x2002fe3c) at ../src/stop_list.c:57
#5  0x0805cb1c in ?? ()
#6  0x0805a962 in ?? ()
#7  0x0801ebca in ?? ()
#8  0x0801e1fa in ?? ()
#9  0x200202d6 in main () at ../src/main.c:23
#10 0x080079de in ?? ()
#11 0x00000000 in ?? ()
```

The `??` entries are inside the pebble firmware; the rest are in the Caltrain
app.
We can step forward a few times to get to an interesting point using the `step`
or `s` command:

```nc|text
(gdb) s
7	  const time_t timestamp = time(NULL);
(gdb) s
8	  const uint16_t minute = current_minute();
(gdb) s
current_minute () at ../src/model.c:183
183	  const time_t timestamp = time(NULL);
(gdb)
```

Now we've stepped into another function, `current_minute`. Let's say we're
confident in the implementation of this (maybe we wrote unit tests), so we can
jump back up to `find_next_train` using the `finish` command:

```nc|text
(gdb) finish
Run till exit from #0  current_minute () at ../src/model.c:183
0x20020f38 in find_next_train (count=184, times=0x2002f414, nb=0x2001873c,
    sb=0x20018738) at ../src/planning.c:8
8	  const uint16_t minute = current_minute();
Value returned is $2 = 738
(gdb)
```

When we step to the next line, we see it has a similar `current_day` that we
don't need to inspect closely, so we jump over it using the `next` or `n`
command:

```nc|text
(gdb) s
9	  const uint8_t day = current_day();
(gdb) n
11	  for(int i = 0; i < count; ++i) {
(gdb)
```

Now we can double check our current state by using `info locals` to look at all
our local variables, and `info args` to look at what was originally passed in:

```nc|text
(gdb) info locals
i = 184
best = {0x0 <__pbl_app_info>, 0x0 <__pbl_app_info>}
timestamp = 1456776942
minute = 738
day = 1 '\001'
(gdb) info args
count = 184
times = 0x2002f414
nb = 0x2001873c
sb = 0x20018738
(gdb)
```

`timestamp`, `minute` and `day` all have the values they gained from our last
few function calls. `best` is still a pair of NULL pointers, and `i` hasn't been
assigned yet, so its value is garbage. Once we step another line it'll be filled
in, which we can check using the `print` or `p` command:

```nc|text
(gdb) s
12	    TrainTime *train_time = &times[i];
(gdb) p i
$3 = 0
```

Now let's step forward and have it fill in `train_time`, and see what we get:

```nc|text
(gdb) s
14	    trip_get(train_time->trip, &trip);
(gdb) p train_time
$4 = (TrainTime *) 0x2002f414
(gdb)
```

This is unenlightening — it's just the same pointer as `times`, which is what we
expect when referencing `&times[0]`. Fortunately, `print`/`p` will evaluate
arbitrary expressions, so we can dereference the pointer to see what it actually
points at:

```nc|text
(gdb) p *train_time
$5 = {trip = 189, time = 309, stop = 13 '\r', sequence = 10 '\n'}
(gdb)
```

Better! It might be more interesting to just print that out for each loop
iteration, so let's set a breakpoint here and have it print `*train_time`
and continue:

```nc|text
(gdb) b
Breakpoint 3 at 0x20020f62: file ../src/planning.c, line 14.
(gdb) commands
Type commands for breakpoint(s) 3, one per line.
End with a line saying just "end".
>p *train_time
>c
>end
(gdb) c
Continuing.

Breakpoint 3, find_next_train (count=184, times=0x2002f414, nb=0x2001873c,
    sb=0x20018738) at ../src/planning.c:14
14	    trip_get(train_time->trip, &trip);
$6 = {trip = 209, time = 344, stop = 13 '\r', sequence = 11 '\v'}

Breakpoint 3, find_next_train (count=184, times=0x2002f414, nb=0x2001873c,
    sb=0x20018738) at ../src/planning.c:14
14	    trip_get(train_time->trip, &trip);
$7 = {trip = 199, time = 345, stop = 13 '\r', sequence = 13 '\r'}
```
…and so on. A bit noisy, so let's remove that breakpoint now:

```nc|text
(gdb) delete 3
(gdb)
```

Finally, let's have our program continue on its way by running `c` again:

```nc|text
(gdb) c
Continuing
```

When we want to get out of gdb we'll need our `(gdb)` prompt back, so press
ctrl-C to pause the app again:

```nc|text
^C
Program received signal SIGINT, Interrupt.
0x08007072 in ?? ()
(gdb)
```

This will most likely pause execution inside some firmware code, as we did when
we initially launched gdb. We can now do anything we've done before, but we're
just going to quit:

```nc|text
(gdb) quit
A debugging session is active.

	Inferior 1 [Remote target] will be killed.

Quit anyway? (y or n) y
katharine@scootaloo ~/p/pebble-caltrain (master)>
```

Hopefully this has given you some ideas as to how you might be able to use gdb
to debug your own apps. If you'd like to know more about gdb,
[here is a Q&A-style tutorial](http://www.unknownroad.com/rtfm/gdbtut/) that
will answer many questions you might have. Good luck and happy debugging!


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Take the Pebble Health API in your Stride
author: jonb
tags:
- Freshly Baked
---

I've been desperate to write a blog post about the Pebble [HealthService API]
(https://developer.pebble.com/docs/c/Foundation/Event_Service/HealthService/)
since it's initial release last month, and now I can finally share its
awesomeness with you. I'm also going to show you how you can use this exciting
new API to build an ultra-cool clone of the [Stride watchface]
(https://apps.getpebble.com/en_US/application/56b15c5c9c4b20ed5300006c) by
Pebble.

## Background

Back in December 2015, Pebble Health was launched and had a massive uptake from
the existing Pebble userbase. For some reason, I foolishly thought health was
purely aimed at fitness fanatics and I completely underestimated its potential
uses and benefits to non-athletes like me. To give you a bit of my background,
I'm an overweight father of two, I don't get much sleep and probably spend an
unhealthy amount time in front of a computer screen. I enabled Pebble Health,
and as time progressed, I began to see how little exercise and how little sleep
I actually get.

Once you have started visualising health information, you can begin to see
patterns and get an understanding of what 'good' days and 'bad' days look like.
You can then make incremental changes and see how that affects your mood and
quality of life, though to be completely honest, I haven't changed my bad habits
just yet. I haven't suddenly started running every day, nor have I magically
started getting >5 hours sleep each night, but I now have a wealth of data
available to help shape my future lifestyle decisions.

## What health data is available?

The HealthService API exposes various [`HealthMetric`]
(https://developer.pebble.com/docs/c/Foundation/Event_Service/HealthService/#HealthMetric)
values which relate to the user being physically active, or sleeping.

- `HealthMetricStepCount` - The number of steps counted.
- `HealthMetricActiveSeconds` - The number of seconds spent
active (i.e. not resting).
- `HealthMetricWalkedDistanceMeters` - The distance
walked, in meters.
- `HealthMetricSleepSeconds` - The number of seconds spent
sleeping.
- `HealthMetricSleepRestfulSeconds` - The number of sleep
seconds in the 'restful' or deep sleep state.
- `HealthMetricActiveKCalories` - The number of kcal
(Calories) burned while active.
- `HealthMetricRestingKCalories` - The number of kcal
(Calories) burned while resting due to resting metabolism.

## Querying the health data

There are a few important things you need to do before you actually attempt to
access the data from the HealthService.

1. Indicate that your watchapp will be accessing Health data by adding `health`
to the `capabilities` section of the `appinfo.json`, or checking the ‘Uses Health’
option in your project settings in CloudPebble. This is used by the Pebble Time
mobile app to inform users who are installing your watchapp that you're going to
be accessing their health data. If you don’t do this and attempt to access a
health api, your app will be terminated.
2. Check if the user has enabled Pebble Health in the Pebble Health app settings
and that there is any health data available. This is done by checking the
[`HealthServiceAccessibilityMask`]
(https://developer.pebble.com/docs/c/Foundation/Event_Service/HealthService/#HealthServiceAccessibilityMask)
using [`health_service_metric_accessible()`]
(https://developer.pebble.com/docs/c/Foundation/Event_Service/HealthService/#health_service_metric_accessible).

Here is a basic example of the steps you should take before attempting to access
health data:

```c
// Between midnight (the start time) and now (the end time)
time_t start = time_start_of_today();
time_t end = time(NULL);

// Check step count data is actually available
bool any_data_available = HealthServiceAccessibilityMaskAvailable &
   health_service_metric_accessible(HealthMetricStepCount, start, end);
```

## Building a Stride clone

![stride](/images/blog/2016-03-07-image03.png)

Stride by Pebble

The Stride watchface displays the current time and your current step count for
today. Around the edge of the screen it displays a bar which is your progress
towards your average daily step count. The yellow line shows your daily average
step count for the current time of day. So as the day progresses, the yellow
line moves towards the end of your total step goal. If you manage to get ahead
of the yellow line, you’re on track to beat your daily step count and to
highlight this, all of the blue elements turn green.

We're going to break down this project into several steps, so you can easily
see what's going on. I'm going to cheat slightly to keep the examples short.
Specifically we'll use emojis instead of the shoe icon, we won't be displaying
the am/pm indicator and we’ll only be drawing circular progress bars on all
platforms.

## Step 1 - Building the basic watchface

![strider-step1](/images/blog/2016-03-07-image00.png)

We'll start by creating a fairly typical structure of a watchface. We
initialise a new `Window`, add a `TextLayer` to display the time, then set up a
tick handler to update the time every minute.

[View the source code]
(https://github.com/pebble-examples/strider-watchface/tree/step1/src/Strider.c)

## Step 2 - Add the step count & icon

![strider-step2](/images/blog/2016-03-07-image07.png)

Now we need to add a new `TextLayer` to display our step count and emoji. We're
going to need to query the HealthService API to retrieve:

- The current step count for today.
- The target step goal amount.
- The average step count for the current time of day.

The step goal only needs to be updated once per day and we're going to use the
[`HealthEventMovementUpdate`]
(https://developer.pebble.com/guides/pebble-apps/sensors/health/#subscribing-to-healthservice-events)
event to trigger an update for the other data. This event will probably trigger
multiple times per minute, so if you’re looking to conserve energy, you could
manually update the data. Stride also changes the color of the text and icon if
the user has exceeded their average step count, so we'll do that too.

[View the source code]
(https://github.com/pebble-examples/strider-watchface/tree/step2/src/Strider.c)

## Step 3 - Add the progress indicators and progress bar

![strider-step3](/images/blog/2016-03-07-image04.png)

We’re going to create 2 new layers, one for the grey progress indicator dots
and the other for the progress bar. To simplify the example, we’re just dealing
with a circular indicator. If you want to see how Stride actually draws
rectangularly, checkout the [health-watchface on Github]
(https://github.com/pebble-examples/health-watchface).

The dots layer update procedure calculates the coordinates for each point using
[`gpoint_from_polar()`]
(https://developer.pebble.com/docs/c/Graphics/Drawing_Primitives/#gpoint_from_polar)
and then draws a circle at that point, for each dot. The progress layer update
procedure uses [`graphics_fill_radial()`]
(https://developer.pebble.com/docs/c/Graphics/Drawing_Primitives/#graphics_fill_radial)
which can fill a circle from a start and end angle, we’re using a narrow inset
thickness so that the circle is just drawn as a ring.

We also need to redraw the progress layer when the step count changes, but all
we need to do is mark the layer dirty.

[View the source code]
(https://github.com/pebble-examples/strider-watchface/tree/step3/src/Strider.c)

## Step 4 - Add the average step indicator

![strider-step4](/images/blog/2016-03-07-image01.png)

The final thing we’re going to add is an indicator to show your average daily
steps for this time of day. We’ve already calculated the average, and we’re
going to use [`graphics_fill_radial()`]
(https://developer.pebble.com/docs/c/Graphics/Drawing_Primitives/#graphics_fill_radial)
again but this time it’s just to draw a yellow line. We’ll need to add another
new layer and update procedure to handle the drawing of the line.

We also need to redraw the new layer when the step average value changes, but
again, all we need to do is mark the layer dirty.

[View the complete source code]
(https://github.com/pebble-examples/strider-watchface)

## The finished product

![strider-step2b](/images/blog/2016-03-07-image05.jpg)

Strider watchface

## Final thoughts

I’ve really only scratched the surface of the HealthService API, but hopefully
you’re now sufficiently excited to build something awesome! We’d really love to
see how you use it, and If you create a health enabled watchapp or watchface,
don’t forget to let us know on [Twitter](http://twitter.com/pebbledev) or on
[Discord]({{ site.links.discord_invite }})!


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Pebble.js Support for Chalk!
author: meiguro
tags:
- Freshly Baked
---

After many morning coding sessions on the weekends,
[Pebble.js](/docs/pebblejs/) has been updated to
support the Chalk platform (Pebble Time Round). This update is available in
[CloudPebble]({{site.links.cloudpebble}}), and ready for you to work with right in
your browser with no setup!

Supporting Chalk required a ton of bug crushing to become compatible with the
new 3.0 style status bar, changes to accommodate reduced memory on the 3.x
Aplite platform, adding new features such as text flow and paging, and making it
easier to target multiple platforms by adding a feature detection API.

![Pebble.js Screenshots](/images/blog/2016-05-13-pebble-js-round/pebblejs.png)

Pebble.js running on all available platforms.

Whether or not you’re targeting the Chalk platform, there are many new features
for you to explore in this update - and simply recompiling your project with the
latest version of Pebble.js will update the look and feel through refined
spacing between text fields.

The background color of window's status bar can now be changed, allowing it to
blend in with the app's background in a similar way to Pebble's Music app.

```js
var defaultBackgroundColor = 'white';
var defaultColor = 'black';

var card = new UI.Card({
  backgroundColor: defaultBackgroundColor,
  status: {
    color: defaultColor,
    backgroundColor: defaultBackgroundColor
  }
});

card.show();
```

Two new elements were added,
[Line](/docs/pebblejs/#line) and
[Radial](/docs/pebblejs/#radial), and all elements
now include [borderWidth](/docs/pebblejs/#element-
borderwidth- width), [borderColor](/docs/pebblejs
/#element- bordercolor-color) and
[backgroundColor](/docs/pebblejs/#element-
backgroundcolor-color) properties (except `Line`, which uses
[strokeWidth](/docs/pebblejs/#line-strokewidth-
width) and [strokeColor](/docs/pebblejs/#line-
strokecolor-color) instead).

There are many bugfixes as well. Scrolling is no longer jittery for cards and
menus, the status bar will no longer disappear upon changing windows that both
requested to have a status bar, and large bodies of text are truncated in cards
instead of just not showing up.

There is one new known issue - which causes some applications with heavy memory
usage to crash. If you're experiencing this issue, we would appreciate you
adding more details to [#161](https://github.com/pebble/pebblejs/issues/161).

This update also comes with two new guides to help familiarize yourself with the
exciting new world of round and colorful apps. [Using
Color](/docs/pebblejs/#using-color) will help you
understand all the different ways you can specify which color you want your text
(and other elements) to be, and how you would go about styling your app with
color. [Using Feature](/docs/pebblejs/#using-
feature) will help you understand the new Feature API, how it can be used to
specify different behaviours and UIs depending on what platform you're running
on, and what features it includes.

## CloudPebble

Enabling Chalk support in CloudPebble is simple. Open your project's "Settings"
screen, check the "Build Chalk" box, then recompile your project.

![CloudPebble Settings Screen](/images/blog/2016-05-13-pebble-js-round/settings.png)

Cloud Pebble Settings Screen.

To run your project on CloudPebble's Chalk emulator, compile your project, then
open the "Compilation" sceen, and select "Emulator" then "Chalk."

## Pebble Tool + Local SDK

If you're using the Pebble Tool and local SDK, you'll need to merge the `master`
branch of the Pebble.js repositoroy into your project, then edit the
`appinfo.json` file to include `chalk` in the `targetPlatforms` array.

```
{
  "uuid": "8e02b5f5-c0fb-450c-a568-47fcaadf97eb",
  "shortName": "Test",
  "longName": "Test Application",
  "companyName": "#makeawesomehappen",
  "versionLabel": "0.1",
  "sdkVersion": "3",
  "enableMultiJS": true,
  "targetPlatforms": ["aplite", "basalt", "chalk"],
  "watchapp": {
    "watchface": false
  },
  "appKeys": { },
  "resources": { }
}

```

To run your project with command line emulator, compile your project, then
install it to the Chalk emulator:

```
> pebble clean
> pebble build && pebble install --emulator=chalk --logs
```

Remember, we’re also working on supporting native JavaScript applications on
Pebble smartwatches, and are calling it
[Rocky.js](https://pebble.github.io/rockyjs/). Rest assured, I will continue to
maintain Pebble.js, and you can continue to develop and ship apps with it! Keep
an eye out on future developments by following the [GitHub
repository](https://github.com/pebble/pebblejs) and checking out the latest
commits.

Let us know abour Pebble.js project or anything else you feel like mentioning,
by shouting on Twitter to [@PebbleDev](https://twitter.com/pebbledev),
[@Pebble](https://twitter.com/pebble) or even myself
[@Meiguro](https://twitter.com/meiguro).

Here’s to more exciting weekend coding sessions in the future for both of us!


---
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

title: Introducing Pebble Packages
author: katharine
tags:
- Freshly Baked
---

I am thrilled to announce that, as part of SDK 3.13 and Pebble Tool 4.3, we have
launched our own packaging system: Pebble Packages!

There are already a healthy selection of Pebble libraries to be had, such as
Yuriy's excellent [EffectLayer](https://github.com/ygalanter/EffectLayer),
Reboot's Ramblings'
[palette manipulator](https://github.com/rebootsramblings/GBitmap-Colour-Palette-Manipulator),
or even our own [weather library](https://github.com/pebble-hacks/owm-weather).
However, using them today is inelegant: you generally have to copy/paste all of
the library code into your own project and follow some other setup instructions:
assigning appmessage keys, editing resources into your appinfo.json, or similar.

Starting today, using a Pebble Package can be as simple as running
`pebble package install pebble-owm-weather`. A Pebble Package can contain
both C code for the watch and JavaScript code for the phone. Furthermore, it can
also contain any resources, and define its own appmessage keys. All numeric
identifiers will be resolved at app build time to ensure there are never any
conflicts. This helps to enable zero-setup packages of all kinds.

To make the usage of appmessages in Pebble Packages possible, we have also
substantially improved the functionality of the old `appKeys` section of
appinfo.json. We will now automatically insert the keys into your C code with
the prefix `MESSAGE_KEY_`. You can also access their numeric values from
JavaScript by requiring the `message_keys` module:
`var keys = require('message_keys')`.

Packages would be nothing without a package manager, so we have built our
packaging system on top of the excellent [npm](https://npmjs.com) package manager. This gives
us support for dependency management, versioning, package hosting, and more. Furthermore,
some traditional JavaScript modules on npm will work out of the box, as long as
they don't depend on running in node or on a browser. As part of this
move we have **deprecated appinfo.json**: we now use `package.json` instead.
The latest version of the Pebble Tool can convert your project when you run
`pebble convert-project`. Old-style projects continue to be supported, but
cannot use the package manager.

For your convenience, we have provided some wrappers around npm functionality:

* `pebble package install` to safely install a package.
* `pebble package uninstall` to uninstall a package.
* `pebble package login` to log in to or create your npm account.
* `pebble package publish` to publish your package to npm.

We also have UI in CloudPebble to enter your dependencies.

You can browse the available packages on npm under the
[pebble-package](https://www.npmjs.com/browse/keyword/pebble-package) keyword,
and read our guides on [using](/guides/pebble-packages/using-packages/) and
[creating](/guides/pebble-packages/creating-packages/) Pebble Packages.

I'm looking forward to seeing what you make!