---
# 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 SDK 3.9-beta5 - Changelog
date: 2016-01-19
---

This is a pre-release SDK, containing a preview of the new Pebble Health
API.

### Known Issues

* `HealthLightLevel` will be renamed to ``AmbientLightLevel`` before SDK 3.9 ships.
* Bluetooth may be more unreliable in this beta release.

### Changes to Firmware

* Resolved confusion caused by manipulating the window stack while it is animating.
* Fixed a crash when enabling text pagination on aplite.
* Improved the reliability of smartstrap communication when the system is busy.
* Fixed calendar pins sometimes omitting the location on aplite.
* Reduced the memory overhead for PNG decompression to 1152 bytes (plus space for both
  the compressed and decompressed versions of the image).
* Fixed an issue causing "call back" on missed call pins on iOS to not actually
  call back.
* Fixed an issue causing sleep graphs to not be drawn at all on the deep sleep
  screen in the Health app unless there was deep sleep to display.
* Multiple timezone fixes, including Uruguay and Cancun.
* Made system watchface transitions snappier on Pebble Time.
* Redesigned the watch-only mode watchface.
* Changed the low-battery percentage warnings to time estimates.
* Added U+1F3B5 MUSICAL NOTE 🎵 and U+1F381 WRAPPED PRESENT 🎁 to the notification
  font.
* Added an indicator of picture notifications on iOS.
* Redesigned Tic-Toc on the Rose Gold Pebble Time Round.
* Moved the hands on Tic-Toc on Pebble Time Round one pixel to the left.
* Assorted health tracking improvements.
* Assorted stability improvements.
* Assorted localization improvements.

### Changes to SDK

* Added the new [Health API](``HealthService``)!
  * This enables you to read information from Pebble Health and include it in your
    app or watchface.
  * We are interested in feedback on this API! Let us know what you think
    ~~on Slack or by contacting developer support~~ on [Discord]({{ site.links.discord_invite }})!
* Fixed an issue introduced in 3.8.2 causing some _successful_ font builds to hang
  the build process indefinitely.
* Added ``PBL_IF_MICROPHONE_ELSE`` and ``PBL_IF_SMARTSTRAP_ELSE`` macros, for consistency
  with ``PBL_IF_COLOR_ELSE`` and friends.


---
# 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 SDK 3.9-beta7 - Changelog
date: 2016-01-29
---

This is a pre-release SDK, containing a preview of the new Pebble Health
API.

The following release notes list only changes since [3.9-beta5](/sdk/changelogs/3.9-beta5/)

### Changes to Firmware

* Fixed a crash when calling ``health_service_any_activity_accessible``.
* Ensured that ``HealthEventSignificantUpdate`` is called immediately when subscribing
  to the health service, as documented.
* Fixed a failure to correctly read the ambient light sensor.
* Further watchface transition tweaks.
* Improved firmware stability.
* Improved bluetooth reliability.

### Changes to SDK

* `HealthLightLevel` was renamed to ``AmbientLightLevel``
* Added `PBL_HEALTH` and `PBL_IF_HEALTH_ELSE` macros.
* Added support for multiple JavaScript files! Check out
  [the blog post](/blog/2016/01/29/Multiple-JavaScript-Files/) for more details.

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: Pebble SDK 3.9-beta8 - Changelog
date: 2016-02-01
---

This is a pre-release SDK, containing a preview of the new Pebble Health
API.

**Important note**: We have changed the values of ``HealthServiceAccessibilityMask``. This is
not a binary compatible change, so you _must_ recompile watchfaces that use
``health_service_metric_accessible`` or ``health_service_any_activity_accessible``.
Additionally, ``HealthServiceAccessibilityMaskAvailable`` is no longer zero, so code that
tried to compare with zero will need to be updated.

The following release notes list only changes since [3.9-beta7](/sdk/changelogs/3.9-beta7/).

### Changes to Firmware

* Fixed ``health_service_get_minute_history`` sometimes returning empty minutes.
* Fixed a bug causing the backlight to get stuck on at full intensity.
* Reduced watchface transition animation choppiness.
* Improved firmware stability.

### Changes to SDK

* Changed the values of ``HealthServiceAccessibilityMask` so that
  ``HealthServiceAccessibilityMaskAvailable`` is nonzero. This is an ABI incompatible change,
  and watchfaces must be recompiled with beta8 to run correctly on beta8 or later
  firmware.
* Added support for `require`ing a JSON file from PebbleKit JS, if the wscript is adjusted
  appropriately.


---
# 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 SDK 3.9.1 - Changelog
date: 2016-02-04
---

### Changes to Firmware

* Fixed a number of DST-related timezone errors introduced in 3.9.

### Changes to SDK

None; no SDK was shipped.


---
# 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 SDK 3.9.2 - Changelog
date: 2016-02-09
---

### Changes to Firmware

* Resolved the backlight not activating on Kickstarter-era Pebbles on which the
  backlight intensity had never been set.
* Fixed a potential crash after updating the firmware with a low battery.

### Changes to SDK

* Fixed an error when attempting to `require()` JSON files.


---
# 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 SDK 3.9 - Changelog
date: 2016-02-03
---

### Changes to Firmware

* Resolved confusion caused by manipulating the window stack while it is animating.
* Fixed a crash when enabling text pagination on aplite.
* Improved the reliability of smartstrap communication when the system is busy.
* Fixed calendar pins sometimes omitting the location on aplite.
* Reduced the memory overhead for PNG decompression to 1152 bytes (plus space for both
  the compressed and decompressed versions of the image).
* Fixed an issue causing "call back" on missed call pins on iOS to not actually
  call back.
* Fixed an issue causing sleep graphs to not be drawn at all on the deep sleep
  screen in the Health app unless there was deep sleep to display.
* Multiple timezone fixes, including Uruguay and Cancun.
* Made system watchface transitions snappier on Pebble Time.
* Redesigned the watch-only mode watchface.
* Changed the low-battery percentage warnings to time estimates.
* Added U+1F3B5 MUSICAL NOTE 🎵 and U+1F381 WRAPPED PRESENT 🎁 to the notification
  font.
* Added an indicator of picture notifications on iOS.
* Redesigned Tic-Toc on the Rose Gold Pebble Time Round.
* Moved the hands on Tic-Toc on Pebble Time Round one pixel to the left.
* Assorted health tracking improvements.
* Assorted stability improvements.
* Assorted localization improvements.

### Changes to SDK

* Added the new [Health API](/guides/events-and-services/health/)!
  * This enables you to read information from Pebble Health and include it in your
    app or watchface.
* Added support for multiple JavaScript files! Check out
  [the blog post](/blog/2016/01/29/Multiple-JavaScript-Files/) for more details.
* Fixed an issue introduced in 3.8.2 causing some _successful_ font builds to hang
  the build process indefinitely.
* Added ``PBL_IF_MICROPHONE_ELSE``, ``PBL_IF_SMARTSTRAP_ELSE``, and
  ``PBL_IF_HEALTH_ELSE`` macros, for consistency with ``PBL_IF_COLOR_ELSE`` and
  friends. This also includes new `PBL_MICROPHONE`, `PBL_SMARTSTRAP`, and
  `PBL_HEALTH` defines.


---
# 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 SDK 4.0-beta17 - Changelog
date: 2016-08-16
---

This is a developer preview release of SDK 4.0. No firmware is provided.

### Changes to Firmware

* Added support for [running developer apps written in JavaScript](/blog/2016/08/15/introducing-rockyjs-watchfaces/)

### Changes to SDK

* AppGlance icons must now be provided as references to `publishedMedia`
  in package.json. Providing plain resource IDs will not work.
* Custom timeline icons can now be created via `publishedMedia`.
* Added support initial for building JavaScript apps. These apps won't
  support aplite.

### Known Issues

* `publishedMedia` is completely undocumented. Updated documentation will
  be available soon. In the interim, continue using 4.0-dp3 to develop
  appglances.


---
# 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 SDK 4.0-dp1 - Changelog
date: 2016-06-15
---

This is the first Developer Preview release of the brand new Pebble SDK 4.0.
This changelog collects only changes visible to developers using the emulator.
A complete changelog will be provided when 4.0 ships on physical watches.

### Changes to Firmware

* Added support for [Pebble 2](https://www.kickstarter.com/projects/597507018/pebble-2-time-2-and-core-an-entirely-new-3g-ultra).
* Added [AppGlances](/guides/user-interfaces/appglance-c/) and Timeline
~~Peek~~ Quick View.

### Changes to SDK

* Added the new "Diorite" platform for the Pebble 2.
* Added `PBL_COMPASS`, `PBL_SMARTSTRAP_POWER`, and `PBL_PLATFORM_DIORITE`
  defines.
* Added ``preferred_result_display_duration`` to get the recommended number of
  milliseconds a result window should be visible before it should closed.
* Added ``AppExitReason`` and ``exit_reason_set``
for an application to be able to notify the system of
[the reason it is exiting](/guides/user-interfaces/app-exit-reason/).
* Added ``AppGlanceSlice``, ``AppGlanceResult``, ``AppGlanceReloadSession``,
``app_glance_add_slice``, ``AppGlanceReloadCallback`` and ``app_glance_reload``.
to support [AppGlances](/guides/user-interfaces/appglance-c/).
* Added [Unobstructed Area APIs](/guides/user-interfaces/unobstructed-area/):
``UnobstructedAreaWillChangeHandler``,
``UnobstructedAreaChangeHandler``,
``UnobstructedAreaDidChangeHandler``,
``UnobstructedAreaHandlers``,
``layer_get_unobstructed_bounds``,
``unobstructed_area_service_subscribe``
and ``unobstructed_area_service_unsubscribe``
to enable a watchface to adapt to overlays partially obstructing it, such as
during a Timeline ~~Peek~~ Quick View.
* Added ``HealthMetricAlert``, ``HealthAggregation``, ``health_service_peek_current_value``,
  ``health_service_peek_current_value``, ``health_service_aggregate_averaged``,
  ``health_service_aggregate_averaged``,
  ``health_service_metric_aggregate_averaged_accessible``,
  ``health_service_register_metric_alert``,
  ``health_service_register_metric_alert`` and
  ``health_service_cancel_metric_alert`` in preparation for heart rate support.
  Note that these are not yet implemented.
* Report memory usage for Pebble Packages at build time.

### Changes to Documentation

* Added [AppGlances Guide](/guides/user-interfaces/appglance-c/)
* Added [Unobstructed Area Guide](/guides/user-interfaces/unobstructed-area/)
* Added [AppExitReason Guide](/guides/user-interfaces/app-exit-reason)
* Added [One Click Action Guide](/guides/design-and-interaction/one-click-actions/)
* Added API documentation for new ``HealthService``, ``App Glance``, ``UnobstructedArea`` and ``AppExitReason`` APIs.

### Known Issues

* Creating an ``AppGlanceSlice`` with a .expiration_time of
  `APP_GLANCE_SLICE_NO_EXPIRATION_TIME` results in an AppGlanceSlice that is
  never displayed.
* The ``UnobstructedAreaHandlers`` object must be created before being passed into
  the ``unobstructed_area_service_subscribe`` method.
* The Diorite emulator is still a little shy and will occassionally shake upon
  starting. If your emulator doesn't stop shaking, try installing an app - that
  usually brings it out of its shell.


---
# 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 SDK 4.0-dp2 - Changelog
date: 2016-06-22
---

This is a developer preview release of SDK 4.0. No firmware is provided.

### Changes to Firmware

* Fixed a bug causing ``APP_GLANCE_SLICE_NO_EXPIRATION`` to mean "expire immediately"
  rather than the intended "expire never".
* Fixed a bug causing empty app glances (i.e. attempts to remove all glances) to be
  ignored.

### Changes to SDK

* None.

### Known Issues

* The `UnobstructedAreaHandler` object must be created before being passed into
  the `unobstructed_area_service_subscribe` method.
* The Diorite emulator is still a little shy and will occassionally shake upon
  starting. If your emulator doesn't stop shaking, try installing an app - that
  usually brings it out of its shell.


---
# 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 SDK 4.0-dp3 - Changelog
date: 2016-07-06
---

This is a developer preview release of SDK 4.0. No firmware is provided.

### Changes to Firmware

* Fixed diorite vibrating on boot until cancelled.
* App icons can now be provided in PDC format.
* Changed the watchfaces icon!

### Changes to SDK

* Renamed AppGlanceSlice.layout's `template_string` to
  `subtitle_template_string` and `icon_resource_id` to `icon`. You will need to
  update your apps appropriately for them to compile.
* The compass service will now return ``CompassStatusUnavailable`` on diorite
  watches, which do not have a compass. Apps built with SDK 2 or SDK 3 will
  still see ``CompassStatusDataInvalid``.
* Added a ``PBL_API_EXISTS`` macro, for checking if an API is available in a
  the current SDK at compile time. This macro was also added to SDK 3.14.
* Fixed a bug that caused messageKeys containing an underscore to be mangled.

### Known Issues

* The `UnobstructedAreaHandler` object must be created before being passed into
  the `unobstructed_area_service_subscribe` method.


---
# 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 SDK 4.0-rc20 - Changelog
date: 2016-08-29
---

This is a developer release candidate for SDK 4.0. No firmware is provided.

### Changes to Firmware

* Apps now correctly launch when installed in the SDK shell.
* Rocky bugfixes.
* Other bugfixes.

### Changes to SDK

* This SDK will not install in any pebble tool prior to 4.4-rc1.
* Corrected outdated AppGlance-related definitions in the aplite compatibility
  header.

### Known Issues

* Using system icons for appglances is currently broken. Using custom icons for
  them, however, works fine.
* `pebble gdb` is currently broken.


---
# 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 SDK 4.0.1 - Changelog
date: 2016-09-06
---

This is a hotfix for [SDK 4.0](/sdk/changelogs/4.0/).
The #pebblepast will be televized.

### Changes to Firmware

* Timeline Past can now be assigned to a Quick Launch button.
* Fixed an issue causing the Tylt Vü to not function.
* Fixed a number of issues with AppGlance icons.

### Changes to SDK

* Restored the functionality of `pebble gdb` (requires pebble tool 4.4.1)

### Known issues

* Timeline Past is still not available in the emulator; it will be restored in
  a later update.


---
# 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 SDK 4.0 - Changelog
date: 2016-08-30
---

### Changes to Firmware

* Added support for [Pebble 2]({{ site.links.kickstarter3 }}).
* Added [AppGlances](/guides/user-interfaces/appglance-c/) and [Timeline Quick View](/guides/user-interfaces/unobstructed-area/).
* Removed Timeline Past.

### Changes to SDK

* Added the new "Diorite" platform for the Pebble 2.
* Added support for writing watchfaces in JavaScript running directly on the watch using
  [Rocky.js](/blog/2016/08/15/introducing-rockyjs-watchfaces/).
* Added `PBL_COMPASS`, `PBL_SMARTSTRAP_POWER`, and `PBL_PLATFORM_DIORITE`
  defines.
* Added ``preferred_result_display_duration`` to get the recommended number of
  milliseconds a result window should be visible before it should be closed.
* Added ``AppExitReason`` and ``exit_reason_set``
for an application to be able to notify the system of
[the reason it is exiting](/guides/user-interfaces/app-exit-reason/).
* Added ``AppGlanceSlice``, ``AppGlanceResult``, ``AppGlanceReloadSession``,
``app_glance_add_slice``, ``AppGlanceReloadCallback`` and ``app_glance_reload``.
to support [AppGlances](/guides/user-interfaces/appglance-c/).
* Added [Unobstructed Area APIs](/guides/user-interfaces/unobstructed-area/):
``UnobstructedAreaWillChangeHandler``,
``UnobstructedAreaChangeHandler``,
``UnobstructedAreaDidChangeHandler``,
``UnobstructedAreaHandlers``,
``layer_get_unobstructed_bounds``,
``unobstructed_area_service_subscribe``
and ``unobstructed_area_service_unsubscribe``
to enable a watchface to adapt to overlays partially obstructing it, such as
during a Timeline Quick View.
* The compass service will now return ``CompassStatusUnavailable`` on diorite
  watches, which do not have a compass. Apps built with SDK 2 or SDK 3 will
  still see ``CompassStatusDataInvalid``.
* Report memory usage for Pebble Packages at build time.
* Fixed bug causing zero-length JS files to result in build failures.

### Changes to Documentation

* Added [AppGlances Guide](/guides/user-interfaces/appglance-c/)
* Added [Unobstructed Area Guide](/guides/user-interfaces/unobstructed-area/)
* Added [AppExitReason Guide](/guides/user-interfaces/app-exit-reason)
* Added [One Click Action Guide](/guides/design-and-interaction/one-click-actions/)
* Added API documentation for new ``App Glance``, ``UnobstructedArea`` and ``AppExitReason`` APIs.

### Known Issues

* `pebble gdb` is currently broken.
* AppGlances cannot currently use system icons.
* Custom AppGlance icons may not work if the assigned published media ID does not happen
  to also be a standard resource ID. This can usually be worked around by assigning published
  media IDs starting at 1 and counting up.
* For apps not using AppGlances, the system default icon may appear instead of the app-specified
  menu icon if the resources defined for the app vary between platforms. This can be worked
  around by defining the menu icon resource before defining any resources that use the
  `targetPlatforms` attribute.


---
# 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 SDK 4.1.1 - Changelog
date: 2016-09-29
---

This is a hotfix for [SDK 4.1](/sdk/changelogs/4.1/). No corresponding firmware exists.

### Changes to Firmware

None.

### Changes to SDK

* Fixed a build failure when using only tagged icons.
* Fixed a failure to validate the correct icon or published resource when using tagged resources.

### Known issues

* ``health_service_metric_accessible`` always returns `false` when
  checking the accessibility of ``HealthMetricHeartRateBPM``.
  Instead, use
  `health_service_metric_aggregate_averaged_accessible(HealthMetricHeartRateBPM, time_start, time_end, HealthAggregationAvg, HealthServiceTimeScopeOnce)`


---
# 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 SDK 4.1.2 and 4.1.3 - Changelog
date: 2016-10-04
---

4.1.2 is a hotfix for [firmware 4.1](/sdk/changelogs/4.1/).
4.1.3 removes some debug apps that were inadvertently included in 4.1.2.

### Changes to Firmware

* Fixed an issue where the Rocky.js `Date` constructor did not work for dates in October
  on a leap year. This also fixes an issue where Tic-Toc would fail after one minute.

### Changes to SDK

None.

### Known issues

* ``health_service_metric_accessible`` always returns `false` when
  checking the accessibility of ``HealthMetricHeartRateBPM``.
  Instead, use
  `health_service_metric_aggregate_averaged_accessible(HealthMetricHeartRateBPM, time_start, time_end, HealthAggregationAvg, HealthServiceTimeScopeOnce)`


---
# 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 SDK 4.1.4 - Changelog
date: 2016-10-05
---

This is a hotfix for [SDK 4.1](/sdk/changelogs/4.1/). No corresponding firmware exists.

### Changes to Firmware

None.

### Changes to SDK

* Fixed a build failure when using only tagged icons.
* Fixed a failure to validate the correct icon or published resource when using tagged resources.

(This is the same as 4.1.1, but the fixes were inadvertently reverted in 4.1.2.)

### Known issues

* ``health_service_metric_accessible`` always returns `false` when
  checking the accessibility of ``HealthMetricHeartRateBPM``.
  Instead, use
  `health_service_metric_aggregate_averaged_accessible(HealthMetricHeartRateBPM, time_start, time_end, HealthAggregationAvg, HealthServiceTimeScopeOnce)`


---
# 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 SDK 4.1 - Changelog
date: 2016-09-27
---

### Changes to Firmware

* Added support for Pebble 2 and the Diorite platform.
* New and improved launcher animations for rectangular watches.
* Fully implemented heartrate APIs.
* Fixed a crash when using the compass in the emulator.
* Fixed a _different_ crash when using the compass on a real watch.
* Fixed a crash when using templated AppGlance strings on Pebble 2 watches.
* Fixed Rocky apps crashing on Chalk.
* Improved launcher performance.
* Updated the timezone database.

### Changes to SDK

* Added some missing heartrate-related aplite compatibility macros.
* Added Pebble 2 color codes to ``WatchInfoModel``.
* Timeline past is now accessible in the SDK shell using the up button.
* Oversized menu icons will now fail at build time instead of runtime.
* `publishedMedia` can now reference resources inside Pebble Packages.

### Known issues

* If you use platform tags on your menu icon (e.g. `icon~bw.png` and `icon~color.png`,
  but no `icon.png`), your build will fail. You can work around this by creating an
  unused, un-tagged icon. This file will be validated, so it must be a valid icon.
* ``health_service_metric_accessible`` always returns `false` when
  checking the accessibility of ``HealthMetricHeartRateBPM``.
  Instead, use
  `health_service_metric_aggregate_averaged_accessible(HealthMetricHeartRateBPM, time_start, time_end, HealthAggregationAvg, HealthServiceTimeScopeOnce)`


---
# 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 SDK 4.2-beta4 - Changelog
date: 2016-10-12
---

This is a developer preview for SDK 4.2. No firmware is provided.

### Changes to Firmware

* Emery can now run apps compiled for Basalt (SDK 4.1 or earlier) or Aplite
  (SDK 3.7 or earlier) in "bezel mode".
* Fixed ``health_service_metric_accessible`` for heartrate-related metrics.
* Rocky.js: The reliability of the ``postMessage`` API has been improved.
* Rocky.js: `postmessageconnected`, `postmessagedisconnected` and `postmessageerror`
  events have been added.
* Rocky.js: Using regular expressions no longer results in substantial app log spam.
* Rocky.js: The default app template has been improved.
* Rocky.js: The coordinate system was adjusted by 0.5 pixels; (0, 0) now refers to
  top left of the first pixel, rather than the center of the pixel.
* Rocky.js: The `memorypressure` event has been added. When fired with
  `{level: 'high'}`, the app must free up sufficient memory or it will be
  terminated.
* Rocky.js: Content size is exposed via ``UserPreferences``.
* Rocky.js: `watchInfo.`platform now works as expected in the emulator.
* Rocky.js: Removed the global `print()` function; use `console.log()`.
* Rocky.js: Fixed a crash when passing invalid values to `clearTimeout` or
  `clearInterval`.
* Rocky.js: Provided constructors for `CanvasRenderingContext2D`, `RockyCanvasElement`
  and `Event`.
* Rocky.js: Removed unimplemented methods that previously threw an exception when called,
  allowing for polyfills of those methods.
* Rocky.js: Added the ability to unsubscribe from events using ``removeEventListener`` or
  ``off``.
* Bug fixes and improvements.

### Changes to SDK

* Added support for the Emery platform.
* Rocky.js is now considered stable, and can be used to submit apps to the appstore.
* Added ``preferred_content_size()`` to retrieve the user's preferred font size.
* All JS bundling is now performed using [webpack](https://webpack.github.io)
  * Only JavaScript files that are actually used are now bundled.

### Known Issues

* Clay does not work after building with SDK 4.2-beta4.
* Apps that use an external bundler and therefore expect `require` to exist at
  runtime in order to `require('message_keys')` will not work.


---
# 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 SDK 4.2-beta5 - Changelog
date: 2016-10-14
---

This is a developer preview for SDK 4.2. No firmware is provided.

### Changes to Firmware

* Rocky.js: assorted postMessage fixes.

### Changes to SDK

* Fixed apps that use an external bundler and expect `require` to exist at runtime.
  * This also fixes Clay.


---
# 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 SDK 4.2.1 - Changelog
date: 2016-10-19
---

This is a hotfix for [SDK 4.2](/sdk/changelogs/4.2/). No corresponding firmware exists.

### Changes to Firmware

None.

### Changes to SDK

* Removed the use of `eval` in JavaScript bundling that was causing incorrect behavior
  when running in the emulator and on iOS.

### Known issues

* JavaScript errors do not have a useful filename or line number. You may find it helpful
  to look up the line in build/pebble-js-app.js to determine what the error is referring to.


---
# 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 SDK 4.2.2 - Changelog
date: 2016-11-02
---

This is a hotfix for [SDK 4.2](/sdk/changelogs/4.2/). No corresponding firmware exists.

### Changes to Firmware

* The glyph size limit for Emery has been increased. It will be increased for Basalt,
  Chalk, and Diorite in a future update.

### Changes to SDK

* App builds now generate a sourcemap for PebbleKit JS code, which the pebble tool can
  use to resolve line numbers in debug output.


---
# 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 SDK 4.2 - Changelog
date: 2016-10-18
---

### Changes to Firmware

* Emery can now run apps compiled for Basalt (SDK 4.1 or earlier) or Aplite
  (SDK 3.7 or earlier) in "bezel mode".
* Fixed ``health_service_metric_accessible`` for heartrate-related metrics.
  * Note that, as heartrate data is only retained for two hours, HRM accessibility
    checks going back further than two hours will always return `false`.
* Rocky.js: The reliability of the ``postMessage`` API has been improved.
* Rocky.js: `postmessageconnected`, `postmessagedisconnected` and `postmessageerror`
  events have been added.
* Rocky.js: Using regular expressions no longer results in substantial app log spam.
* Rocky.js: The coordinate system was adjusted by 0.5 pixels; (0, 0) now refers to
  top left of the first pixel, rather than the center of the pixel.
* Rocky.js: The `memorypressure` event has been added. When fired with
  `{level: 'high'}`, the app must free up sufficient memory or it will be
  terminated.
* Rocky.js: Content size is exposed via ``UserPreferences``.
* Rocky.js: `watchInfo.platform` now works as expected in the emulator.
* Rocky.js: Removed the global `print()` function; use `console.log()`.
* Rocky.js: Fixed a crash when passing invalid values to `clearTimeout` or
  `clearInterval`.
* Rocky.js: Provided constructors for `CanvasRenderingContext2D`, `RockyCanvasElement`
  and `Event`.
* Rocky.js: Removed unimplemented methods that previously threw an exception when called,
  allowing for polyfills of those methods.
* Rocky.js: Added the ability to unsubscribe from events using ``removeEventListener`` or
  ``off``.
* Bug fixes and improvements.

### Changes to SDK

* Added support for the Emery platform.
* Rocky.js is now considered stable, and can be used to submit apps to the appstore.
* Rocky.js: The default app template has been improved.
* Added ``preferred_content_size()`` to retrieve the user's preferred font size.
* All JS bundling is now performed using [webpack](https://webpack.github.io)
  * Only JavaScript files that are actually used are now bundled.


---
# 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 SDK 4.3 - Changelog
date: 2016-11-23
---

### Changes to Firmware

* Added heart rate zones to the Health app.
* Added a heart rate measurement characteristic to the Pebble GATT profile, enabling
mobile apps to retrieve live heart rate data from Pebble watches with a built-in heart
rate monitor.
* Added ``HealthMetricHeartRateRawBPM`` to allow access to raw heart rate measurements
from supported watches.
* Added Kickstart watchface.
* Added the display of heart rate measurements in the sports app when a 3rd party app
uses the Sports API.
* Added voice reminders on Pebble for iOS users.
* Improved measurements of heart rate on Pebble 2 devices.
* Bug fixes and improvements.

### Changes to SDK

* Fixed a bug where the Rocky.js `minutechange` event was not emitted when the hour or
day changed.
* Added ``quiet_time_is_active`` to permit C apps to check if Quiet Time is active on a
watch.
* Added the alias `app_package.json` to the webpack.config.js file, to allow Pebble
Packages to `require('app_package.json')`, which will require an app's package.json or
appinfo.json file during JS bundling.
* Added support for specifying custom PebbleKit JS and Rocky.js entry points from
within a Rocky.js project's package.json file.

### Changes to PebbleKit Android (v4.0.1)

* Added new APIs for sending custom HR data and a custom label/value from a mobile companion
app, using the Sports API.
* Added a helper object that will automatically manage state and message construction for
updates to the Sports app on a Pebble watch.
* Updated the Constant ``ICON_MAX_DIMENSIONS`` to be 25px, to be consistent with watches
running 4.0 firmware. If your provided icon exceeds 25px x 25px, you will encounter a runtime
error when calling ``customizeWatchApp``.

### Changes to PebbleKit iOS (v4.0.0)

* Added support for Diorite and Emery watches.
* Added new APIs for sending custom HR data and a custom label/value from a mobile app.
* Added a helper object that will automatically manage state and message construction for
updates to the Sports app on a Pebble watch.
* Removed Bluetooth Classic support. Mobile apps compiled with this version of PebbleKit
will only communicate with watches running FW 3.8 and later, which use BLE for
communications. Mobile apps using PebbleKit iOS 4.0.0 and later will no longer need to be
whitelisted for use with Pebble.


---
# 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.

layout: guides/default
title: Developer Guides
description: |
  Details of all the guides available for use for building watchapps and
  companion phone apps.
permalink: /guides/
hide_comments: true
generate_toc: false
---

This is the main page for the Developer Guides, containing a large number of
resources for developing all kinds of Pebble watchapps. New and experienced
developers can use these resources to help build their skill set when writing
for Pebble.

## What's Here?

To help guide towards the most relevant information, the developer guides are
split up into the following sections. A complete list of every available guide
is available in the [Table of Contents](/guides/toc).

{% for category in site.data.guide-categories %}
### {{ category.title }}
{% for group in site.data.guides %}
  {% assign grp = group[1] %}
  {% if grp.category == category.id %}
  * [**{{ grp.title }}**]({{ grp.url }}) - {{ grp.description }}
  {% endif %}
{% endfor %}
{% endfor %}


---
# 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.

author: thomas
tags:
- Beautiful Code
---

_(2014 09: This article was updated to add links for SDK 2.0 users and add the resource identifier of the fonts as suggested by Timothy Gray in the comments.)_

Just like any modern platform, typography is a very important element of Pebble user interface. As Designers and Developers, you have the choice to use one of Pebble system fonts or to embed your own.

Pebble system fonts were chosen for their readability and excellent quality on Pebble black & white display, it's a good idea to know them well before choosing to embed your own.

## Using Pebble system fonts
To use one of the system font, you need to call `fonts_get_system_font()` and pass it the name of a system font. Those are defined in `pebble_fonts.h` in the `include` directory of your project.

This function returns a `GFont` object that you can use with `text_layer_set_text()` and `graphics_text_draw()`.

For example, in our Hello World example we used the _Roboto Condensed 21_ font:

```c
void handle_init(AppContextRef ctx) {
  window_init(&window, "Window Name");
  window_stack_push(&window, true /* Animated */);

  text_layer_init(&hello_layer, GRect(0, 65, 144, 30));
  text_layer_set_text_alignment(&hello_layer, GTextAlignmentCenter);
  text_layer_set_text(&hello_layer, "Hello World!");
  text_layer_set_font(&hello_layer, fonts_get_system_font(FONT_KEY_ROBOTO_CONDENSED_21));
  layer_add_child(&window.layer, &hello_layer.layer);
}
```

> **Update for SDK 2 users**:
>
> Refer to the [second part of the watchface tutorial](/getting-started/watchface-tutorial/part2/) for updated sample code.

## An illustrated list of Pebble system fonts

{% alert notice %}
A more up-to-date list of fonts can be found by reading {% guide_link app-resources/system-fonts %}
in the developer guides.
{% endalert %}

To facilitate your choice, here is a series of screenshot of Pebble system fonts used to display digits and some text.

{% comment %}HTML below is acceptable according to Matthew{% endcomment %}

    Gothic 14

Gothic 14 Bold

Gothic 18

Gothic 18 Bold

Gothic 24

Gothic 24 Bold

Gothic 28

Gothic 28 Bold

Bitham 30 Black

Bitham 42 Bold

Bitham 42 Light

Bitham 34 Medium Numbers

Bitham 42 Medium Numbers

Roboto 21 Condensed

Roboto 49 Bold Subset

Droid 28 Bold

## A word of caution

To save space on Pebble's memory, some of the system fonts only contain a subset of the default character set:

 * _Roboto 49 Bold Subset_ only contains digits and a colon;
 * _Bitham 34/42 Medium Numbers_ only contain digits and a colon;
 * _Bitham 18/34 Light Subset_ only contains a few characters and you should not use them (this why they are not included in the screenshots).

## Browse the fonts on your watch

To help you decide which font is best, you will probably want to test those fonts directly on a watch.

We have added a new demo application called [`app_font_browser`]({{site.links.examples_org}}/app-font-browser) to the SDK to help you do that. This application uses a ``MenuLayer`` to navigate through the different options and when you have chosen a font, you can use the _Select_ button to cycle through different messages.

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

You can very easily customize the list of messages for your needs and as an exercise for the reader, you can adapt the app to show custom fonts as well!

This example is available in the `Examples/watchapps/app_font_browser` of the SDK and on [Github]({{site.links.examples_org}}/app-font-browser).


---
# 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.

author: team pebble
tags:
- Beautiful Code
---

PebbleKit JavaScript is one of the most popular additions in Pebble SDK 2.0. We are already seeing lots of amazing apps being built that take advantage of http requests, configuration UI, GPS and local storage on the phone.

Here are some Tips & Tricks to get the most out of JavaScript in your Pebble apps.

### Lint it!

Use [JSLint](http://jslint.com/) ([GitHub](https://github.com/douglascrockford/JSLint)) to check that your Javascript is valid. JSLint with default settings has been successful in catching most of the JS errors that have been reported to us.

If you lint it, there's a much better chance that your app will run smoothly on both the Android and iOS platforms.

### Make the best use of your data storage

There are two types of storage that are used with the Pebble, persistent storage and local storage. [Persistent storage](``Storage``) is available through a C API and saves data on Pebble. [Local storage](/guides/communication/using-pebblekit-js) is a JavaScript API and saves data on the phone. These two long-term storage systems are not automatically connected and you will need to use the [AppMessage](``AppMessage``) or [AppSync](``AppSync``) API's to transfer data from one to the other.

Persistent storage is a key-value storage where the values are limited to 256 bytes and the total space used cannot exceed 4 kb. Persistent storage is not erased when you upgrade the app - but it is erased if you uninstall the app. (Please not that this will be true in Pebble SDK 2.0-BETA4 but was not before).

Local storage exists on your mobile phone and will persist between app upgrades. It is important that you periodically review the data that your app is storing locally and remove any data that is unneeded or no longer relevant. This can be especially important when your internal app data structures change in an update to your app, as your new code will be expecting data formatted in a deprecated form.

We strongly recommend that you version your data (adding a key `version` for example) so that a new version of your app can upgrade the format of your data and erase obsolete data..

### Check for ACK/NACK responses

It is imperative that you check for ACK/NACK responses to your last message between your Pebble and phone before initiating another message. Only one message can be sent at any one time reliably and if you try to send additional messages before ACK/NACK responses are received, you will start seeing failed messages and will have to resend them.

[Pebble-faces](https://github.com/pebble-examples/pebble-faces/blob/master/src/js/pebble-js-app.js) is a good example that includes a `transferImageBytes()` function that sends an image to Pebble, waiting for an ACK after each packet.

### Familiarize yourself with the Geolocation API

The W3C has [extensive documentation](http://www.w3.org/TR/geolocation-API/) on how to use the API, as well as best practices.

Please be aware that support of this API is dependent on the underlying OS and results may vary wildly depending on the type of phone, OS version and environment of the user. There are situations where you may not get a response right away, you may get cached data, or you may never get a callback from your phone.

We recommend that you:

 * Always pass an error handler to `getCurrentPosition()` and `watchPosition()` - and test the error scenarios!
 * Set up a timeout (with `setTimeout()`) when you make a geolocation request. Set it to 5 or 10 seconds and let the user know that something is wrong if the timeout fires before any of the callbacks.
 * Take advantage of the different options you can pass to customize the precision and freshness of data.
 * Test what happens if you disable location services on Pebble (on iOS, remove or refuse the authorization to get location, or turn off the iPhone's radio and Wifi ; on Android disable all the location services).
 * On your watch app, communicate to the user when the system is looking for geolocation information, and gracefully handle the situation when Geolocation information is not available.

### Double check your strings for malformed data

Not all valid strings are valid in the context in which they're used. For instance, spaces in an URL are a bad idea and will throw exceptions. (It may also crash the iOS app in some earlier version of the SDK - Dont do it!) JSLint will not catch errors in quoted strings.

### Use the `ready` event

The `ready` event is fired when the JS VM is loaded and ready to run. Do not start network request, try to read localstorage, communication with Pebble or perform similar operations before the `ready` event is fired!

In practice this means that instead of writing:

    console.log("My app has started - Doing stuff...");
    Pebble.showSimpleNotificationOnPebble("BadApp", "I am running!");

You should do:

    Pebble.addEventListener("ready", function() {
      console.log("My app has started - Doing stuff...");
      Pebble.showSimpleNotificationOnPebble("BadApp", "I am running!");
    });

### Do not use recurring timers such as setInterval()

Your Pebble JavaScript can be killed at any time and you should not rely on it to provide long running services. In the future we may also limit the running time of your app and/or limit the use of intervals.

Follow these guidelines to get the most out of long running apps:

 - Design your app so that it can be killed at any time. Save any important configuration or state in local storage when you get it - and reload it in the `ready` event.
 - If you need some function to run at regular intervals, set a timer on Pebble and send a message to the JavaScript. If the JavaScript is dead, the mobile app will re-initialize it and run the message handler.
 - Do not use `setInterval()`. And of course, do not use `setTimeout()` to simulate intervals.

### Identify Users using the PebbleKit JS account token

PebbleKit JavaScript provides a unique account token that is associated with the Pebble account of the current user. The account token is a string that is guaranteed to be identical across devices that belong to the user, but is unique to your app and cannot be used to track users across applications.

If the user is not logged in, the function will return an empty string (""). The account token can be accessed via the `Pebble.getAccountToken()` function call.

Please note that PebbleKit JavaScript does not have access to the serial number of the Pebble connected.

### Determine a User's timezone using a simple JS script

Pebble does not support timezones yet so when Pebble syncs the current time with your phone, it retrieves your local time and stores the local time as a GMT time (ex. 9AM PST -> 9AM GMT), regardless of where you are located in the world.

If you need to get the current timezone in your app, you can use this simple JavaScript code snippet:

    function sendTimezoneToWatch() {
      // Get the number of seconds to add to convert localtime to utc
      var offsetMinutes = new Date().getTimezoneOffset() * 60;
      // Send it to the watch
      Pebble.sendAppMessage({ timezoneOffset: offsetMinutes })
    }

You will need to assign a value to the `timezoneOffset` appKey in your `appinfo.json`:

    {
      ...
      "appKeys": {
        "timezoneOffset": 42,
      },
      ...
    }

And this is how you would use it on Pebble:

    #define KEY_TIMEZONE_OFFSET 42

    static void appmsg_in_received(DictionaryIterator *received, void *context) {

      Tuple *timezone_offset_tuple = dict_find(received, KEY_TIMEZONE_OFFSET);

      if (timezone_offset_tuple) {
        int32_t timezone_offset = timezone_offset_tuple->value->int32;

        // Calculate UTC time
        time_t local, utc;
        time(&local);
        utc = local + timezone_offset;
      }
    }

### Use the source!

We have a growing number of JS examples in the SDK and in our [`pebble-hacks`](https://github.com/pebble-hacks) GitHub account. Check them out!

You can also [search for `Pebble.addEventListener` on GitHub](https://github.com/search?l=&q=Pebble.addEventListener+in%3Afile&ref=advsearch&type=Code) to find more open-source examples.

## Best Practices for the JS Configuration UI

The new configuration UI allows you to display a webview on the phone to configure your Pebble watchface or app. Here are some specific recommendations to development of that UI.

If you have not read it yet, you might want to take a look at our [Using PebbleKit JS for Configuration](/blog/2013/11/21/Using-PebbleKit-JS-Configuration/) blog post.

### Immediately start loading your Web UI

To avoid confusion, make sure UI elements are displayed immediately after the user clicks on settings. This means you should not do any http request or complex processing before calling `Pebble.openURL()` when you get the `showConfiguration` event:

    Pebble.addEventListener("showConfiguration", function() {
      Pebble.openURL('http://www.example.com/mypebbleurl');
    });

If the UI rendering is delayed, the user will get no feedbacks for their action (a press on the configure icon) and this would result in a poor user experience.

### Apply mobile & web best practices

There is a lot of information on the web on how to build webpages that display nicely on mobile devices. iOS and Android both use WebKit based browsers which makes things easier, but it is safe to assume that you will never get the exact same look - and you will always need to test on both platforms. Even if you do not own both devices, you can use the built-in browser in the iOS or Android emulators to test your configuration webpage.

Also consider using framework such as [JQuery Mobile](http://jquerymobile.com/) or [Sencha Touch](http://www.sencha.com/products/touch/) to make your life easier. They provide easy to use components and page templates. If you do not want to write any html, [setpebble.com](http://www.setpebble.com) is another great option that we have [discussed on the blog](/blog/2013/12/02/SetPebble-By-Matt-Clark/) previously.

### Be prepared for the network to be unreachable during a UI configuration attempt

UI configuration HTML is not stored locally on the phone and must be accessed from a hosted server. This introduces situations where a user's phone may not be connected to the network successfully, or your server may be unreachable, in which case the UI configuration screen will not load.

In this scenario, a user will likely cancel the configuration load, which means a blank string is returned to your application `webviewclosed` event. Your watchapp should be designed to handle this such that the watchapp does not erase all settings or crash.

### Display existing settings

When displaying your configuration UI, remember to pass existing settings to the webview and use them to pre-populate the UI!

### Name your configuration submission button "Save"

If you want to ensure that the settings a user has entered are saved correctly, make sure you call your button _Save_ so users understand that it is required to save data. If your configuration page is long, consider putting the _Save_ button at both the top and bottom of the page.

If the user clicks the Exit button (X), a blank string will be passed back to your application. You should be prepared to handle a blank string should a user decide to cancel the configuration process.

### Prompt users to complete app configuration in your Pebble app

Users may not be aware that they need to complete a configuration for your watchapp. It is best to advise users in the watchface or watchapp to go to their phone and configure your app before proceeding.

Use persistent storage or local storage to save a token indicating the user has completed configuration, and consider putting a version string in, to allow you to track which version of your configuration webpage the user has accessed (to assist future upgrades of your app).

## Questions? More tips?

Have more questions? Or want to share more tips? Please post them in the comments below!


---
# 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: Using JSHint for Pebble Development
author: thomas
tags:
- Beautiful Code
---

In our post on [JavaScript Tips and Tricks](/blog/2013/12/20/Pebble-Javascript-Tips-and-Tricks/) we strongly recommended the use of JSLint or [JSHint](http://www.jshint.com/) to help you detect JavaScript problems at compile time.

We believe JSHint can really increase the quality of your code and we will probably enforce JSHint correct-ness for all the apps on the appstore very soon. In this post we show you how to setup jshint and integrate it in your Pebble development process.

## Installing JSHint

JSHint is a modern alternative to JSLint. It is more customizable and you can install it and run it on your machine. This allows you to run JSHint every time you build your Pebble application and fix errors right away. This will save you a lot of install/crash/fix cycles!

JSHint is a [npm package](https://npmjs.org/package/jshint). NPM is included in all recent NodeJS installations. On Mac and Windows you can [download an installer package](http://nodejs.org/download/). On most Linux distribution, you will install nodejs with the standard package manage (`apt-get install nodejs`).

To install the JSHint package, run this command:

    sudo npm install -g jshint

The `-g` flag tells npm to install this package globally, so that the `jshint` command line tool is always available.

## Configuring jshint

JSHint offers a large selection of options to customize its output and disable some rules if you absolutely have to. We have prepared a default configuration file that we recommend for Pebble development. The first half should not be changed as they are the rules that we may enforce on the appstore in the future. The second half is our recommended best practices but you can change them to suit your coding habits.

Save this configuration in a `pebble-jshintrc` file in your Pebble project.

    /*
     * Example jshint configuration file for Pebble development.
     *
     * Check out the full documentation at http://www.jshint.com/docs/options/
     */
    {
      // Declares the existence of a global 'Pebble' object
      "globals": { "Pebble" : true },

      // And standard objects (XMLHttpRequest and console)
      "browser": true,
      "devel": true,

      // Do not mess with standard JavaScript objects (Array, Date, etc)
      "freeze": true,

      // Do not use eval! Keep this warning turned on (ie: false)
      "evil": false,

      /*
       * The options below are more style/developer dependent.
       * Customize to your liking.
       */

      // All variables should be in camelcase
      "camelcase": true,

      // Do not allow blocks without { }
      "curly": true,

      // Prohibits the use of immediate function invocations without wrapping them in parentheses
      "immed": true,

      // Enforce indentation
      "indent": true,

      // Do not use a variable before it's defined
      "latedef": "nofunc",

      // Spot undefined variables
      "undef": "true",

      // Spot unused variables
      "unused": "true"
    }

## Automatically running jshint

To automatically run jshint every time you compile your application, you will need to edit your `wscript` build file. This is the modified `wscript` file:

    # Use the python sh module to run the jshint command
    from sh import jshint

    top = '.'
    out = 'build'

    def options(ctx):
        ctx.load('pebble_sdk')

    def configure(ctx):
        ctx.load('pebble_sdk')
        # Always pass the '--config pebble-jshintrc' option to jshint
        jshint.bake(['--config', 'pebble-jshintrc'])

    def build(ctx):
        ctx.load('pebble_sdk')

        # Run jshint before compiling the app.
        jshint("src/js/pebble-js-app.js")

        ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'),
                        target='pebble-app.elf')

        ctx.pbl_bundle(elf='pebble-app.elf',
                       js=ctx.path.ant_glob('src/js/**/*.js'))

Now simply run `pebble build` and if there is anything wrong in your JS file, the build will stop and the errors will be displayed on the console. You won't believe how much time you will save!

## CloudPebble users

CloudPebble users are encouraged to download and install JSHint on their machine. You can use the [web interface](http://www.jshint.com/) and set most of the recommended options but you will never get 100% correctness because there is no way to declare an extra global variable (the `Pebble` object).

Good news is [CloudPebble is opensource](https://github.com/CloudPebble/CloudPebble/)! We would be happy to offer a brand new [Pebble Steel](http://www.getpebble.com/steel) to the developer who will submit and get through Katherine's approval a pull request to add jshint support in CloudPebble ;)


---
# 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: FreeRTOS™ Code Revisions from Pebble
author: brad
tags:
- Down the Rabbit Hole
---

Today Pebble is releasing its recent modifications to the FreeRTOS project.
Pebble has made a few minor changes to FreeRTOS to enable its new sandboxed
application environment for PebbleOS 2.0 as well as to make Pebble easier to
monitor and debug.

The changes are available
[as a tarball](http://assets.getpebble.com.s3-website-us-east-1.amazonaws.com/dev-portal/FreeRTOS-8.0.0-Pebble.tar.gz)
.

Read on to learn more about the changes and why they were made.

The new PebbleOS 2.0 application sandbox environment allows running third party
native code (Pebble Apps) in a safe and secure manner. In the sandbox, any
application errors should not negatively impact the host operating system.

Because of the hardware restrictions of a Pebble watch, the  implementation is
achieved in a basic manner. Pebble Apps are run in their own FreeRTOS task that
is unprivileged. This means that the instructions the app is authorized to run
are restricted. For example, it’s not allowed to change the `CONTROL` register
(which changes whether the app is privileged or not) or change interrupt
settings. Furthermore, Pebble restricts the memory the app can access to a small
fixed region. This is done using the MPU (Memory Protection Unit) hardware
available in the Pebble microcontroller. Accesses outside of this region will
cause the application to be stopped. This way Pebble can make sure the
application is only interacting with the kernel in ways that will not interfere
with other features and functions.

The FreeRTOS implementation includes a port that uses the MPU (ARM_CM3_MPU)
which is incompatible with the project goals. This port appears meant for safety
critical environments where tasks shouldn’t be allowed to accidentally interact
with each other. However, there doesn’t seem to be any protection from a
malicious task. For example, raising a task's privilege level is as easy as
triggering a software interrupt, which unprivileged tasks are free to use. “MPU
wrapper” functions are provided to allow any task to call a FreeRTOS API
function from any privilege level and operate as a privileged function, ignoring
any MPU settings. The sandbox is intended to restrict how the application
FreeRTOS task is allowed to interact with the kernel, so modifications were
necessary.

To solve this, Pebble has created its own port named ARM_CM3_PEBBLE. This port
is based on the ARM_CM3_MPU port, but is modified to use the MPU in a different
way. No wrappers are provided (you need to be privileged to directly call
FreeRTOS functions) and the `portSVC_RAISE_PRIVILEGE` software interrupt is
removed.

To permit the app to interact with the system in a safe manner, Pebble added a
new software interrupt, called `portSVC_SYSCALL`. Unprivileged code can use it
to jump into the operating system in a privileged state but only using a system-
provided jump-table. The jump-table contains the address to landing functions
(we refer to them as syscalls) that are individually responsible for checking
that the operation is permitted and the parameters are safe and valid.

Pebble has also made some minor changes to how tasks are created and destroyed.
In order for the application to keep working inside its sandbox, it needs access
to certain resources like its own stack memory. FreeRTOS allows a programmer to
specify a buffer to be used as the stack, so a buffer that's allocated inside
the app sandbox memory region is used. However, FreeRTOS has a bug where it
tries to deallocate the stack memory when a task is destroyed, regardless of
where that memory came from. Pebble changed this code to only free the stack
when it was not provided by the system.

Pebble also added the ability for the system to designate its own buffer for the
`_reent` struct. This struct is a feature of the c library we use - newlib -
that contains buffers that are used by various libc functions. For example,
`localtime` returns a pointer to a `struct tm` structure. This struct is
actually stored in the `_reent` struct. FreeRTOS has `_reent` support so which
`_reent` struct is currently being used is swapped around per task, so each task
has their own `_reent` struct to play with and you don’t have issues if two
threads call `localtime` at the same time. To ensure the `_reent` struct for the
application task is available within the sandboxed memory region, Pebble pre-
allocated it and passed it through to `xTaskCreate`.

Finally, Pebble has added some functionality to inspect the saved registers of
tasks that aren’t currently running. This allows Pebble to collect additional
diagnostics if the watch exhibits any errors and to provide basic crash
reporting for apps. For example, App developers will get a dump of the app’s PC
(Program Counter) and LR (Link Register) registers at the time of the crash.

Pebble is incredibly thankful for all the work that the FreeRTOS community has
put into the code. It has been a huge asset when building PebbleOS. We'll be
releasing additional updates as we continue to modify FreeRTOS in the future. If
you have any questions, don’t hesitate to [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: Pebble SDK Tutorial (Part 1)
author: Chris Lewis
excerpt: |
  This is the first in a series of articles that will hopefully help newcomers
  and seasoned programmers alike begin to flex their creative muscles and make
  their own Pebble watchapps. If you prefer a more visual approach, Thomas has
  made videos you can watch to help you get started.
---

##Your First Watchapp

###Introduction

This is the first in a series of articles that will hopefully help newcomers and
seasoned programmers alike begin to flex their creative muscles and make their
own Pebble watchapps. If you prefer a more visual approach, Thomas has made
videos you can watch
[here](https://www.youtube.com/channel/UCFnawAsyEiux7oPWvGPJCJQ) to help you get
started.

Firstly, make sure you are familiar with the following basic C language
concepts. Pebble has [Developer Guides](/guides/) that may help:

- Variables and variable scope (local or global?)
- Functions (definitions and calls)
- Structures
- Pointers
- Program flow (if, else etc.)
- Preprocessor statements (`#define`, `#include` etc.)

Up to speed? Good! Let's create our first watchapp.

###Development Environment
The easiest way to get started is to use [CloudPebble]({{site.links.cloudpebble}})
, a free online IDE for Pebble. This approach requires no installations to start
making watchapps. Advanced users can install the SDK on Linux, OS X or Windows
(via VM such as [VirtualBox](https://www.virtualbox.org/)) for a more hands-on
approach. Instructions for installing the native SDK can be found on the
[Pebble Developer site](/sdk/install/linux/)
. For this tutorial, we will be using CloudPebble.

###First Steps
To get started, log into [CloudPebble]({{site.links.cloudpebble}}) and choose
'Create Project'.

1. Enter a suitable name for the project such as 'My First App'.
2. Set the project type to 'Pebble C SDK'.
3. Set the template as 'Empty project'.
4. Confirm with 'Create'.

To start entering code, choose 'New C file' on the left hand pane. C language
source files typically have the extension '.c'. A suggested standard name is
`main.c`, although this can be anything you like.

###Setting Up the Basics
The power behind the C language comes from its ability to be adapted for many
different devices and platforms, such as the Pebble watch, through the use of
SDKs such as this one. Therefore, to be able to use all the features the Pebble
SDK has to offer us, we must include it at the start of the file by using the
`#include` preprocessor statement (meaning it is processed before the rest of
the code):

```c
#include <pebble.h>
```

All C applications begin with a call to a function called `main()` by the host
operating system, also known as the ‘entry point’ of the program. This must
contain ``app_event_loop()``, and by convention also contains two other
functions to manage the start and end of the watchapp's life:

- `init()` - Used to create (or initialize) our app.
- ``app_event_loop()`` - Handles all events that happen from the start of the
  app to the end of its life.
- `deinit()` - Used to clean up after ourselves and make sure any memory we use
  is free for apps that are run after us.

Following these rules, our first function, `main()` looks like this:

```c
int main(void)
{
  init();
  app_event_loop();
  deinit();
}
```

All functions we call must be defined before they are used so that they are
known to the compiler when the first call is encountered. To this end we will
define our `init()` and `deinit()` functions before they are called in `main()`
by placing them after the `#include` preprocessor statement and before the
definition of `main()` itself. The resulting code file now looks like this:

```c
#include <pebble.h>

void init()
{
  //Create app elements here
}

void deinit()
{
  //Destroy app elements here
}

int main(void)
{
  init();
  app_event_loop();
  deinit();
}
```

###Using the SDK
With the boilerplate app structure done, let's begin using the Pebble C SDK to
create our first watchapp. The first element we will use is the ``Window``. We
will declare our first instance of a ``Window`` outside the scope of any
function in what is known as a 'global variable', before it is first used. The
reason for this is for us to be able to use this reference from any location
within the program. By convention, a global variable's name is prefixed with
`g_` to make it easily identifiable as such. An ideal place for these
declarations is below the preprocessor statements in the source file.

Declare a pointer to a ``Window`` structure to be created later like so:

```c
Window *g_window;
```

At the moment, this pointer does not point anywhere and is unusable. The next
step is to use the ``window_create()`` function to construct a ``Window``
element for the pointer to point to. Once this is done, we also register two
references to the `window_load()` and `window_unload()` functions (known as
handlers) which will manage the creation and destruction of the elements within
that window. This is shown below:

```c
void init()
{
  //Create the app elements here!
  g_window = window_create();
  window_set_window_handlers(g_window, (WindowHandlers) {
    .load = window_load,
    .unload = window_unload,
  });
}
```

The next logical step is to define what we mean by `window_load()` and
`window_unload()`. In a similar fashion to `init()` and `deinit()`, these must
be defined before they are first used; above `init()`, but below the definition
of `g_window` at the top of the file. Think of them as the `init()` and
`deinit()` equivalent functions for the ``Window``:

```c
void window_load(Window *window)
{
  //We will add the creation of the Window's elements here soon!
}

void window_unload(Window *window)
{
  //We will safely destroy the Window's elements here!
}
```

This modular approach to app creation allows a developer to create apps with all
relevant sub-elements managed within the life of that particular ``Window``.
This process is shown visually in the diagram below:

![Lifecycle](/images/blog/tut_1_lifecycle.png "Lifecycle")

As a responsible app developer, it is a good practice to free up any memory we
use to the system when our app is closed. Pebble SDK elements use functions
suffixed with `_destroy` for this purpose, which can be done for our ``Window``
element in `deinit()`:

```c
void deinit()
{
  //We will safely destroy the Window's elements here!
  window_destroy(g_window);
}
```

The final step in this process is to make the app actually appear when it is
chosen from the Pebble menu. To do this we push our window to the top of the
window stack using ``window_stack_push()``, placing it in the foreground in
front of the user. This is done after the ``Window`` is created, and so we will
place this call at the end of `init()`:

```c
window_stack_push(g_window, true);
```

The second argument denotes whether we want the push to be animated. This looks
cool, so we use `true`! The app will now launch, but will be completely blank.
Let's rectify that.

###Displaying Content
So far we have created and pushed an empty ``Window`` element. So far so good.
Now let's add our first sub-element to make it show some text. Introducing the
``TextLayer``! This is an element used to show any standard string of characters
in a pre-defined area, called a 'frame'. As with any element in the SDK, we
begin with a global pointer to a structure-to-be of that type:

```c
TextLayer *g_text_layer;
```

The rest of the process of using the ``TextLayer`` is very similar to that of
the ``Window``. This is by design, and means it is easy to tell which functions
to use as they are named in a 'plain English' style. Creating the ``TextLayer``
will be done within the `window_load()` function, as it will be shown within the
parent ``Window`` (here called `g_window`).

The functions we call to perform this setup are almost self-explanatory, but I
will go through them after the following segment. Can you spot the pattern in
the SDK function names?

```c
g_text_layer = text_layer_create(GRect(0, 0, 144, 168));
text_layer_set_background_color(g_text_layer, GColorClear);
text_layer_set_text_color(g_text_layer, GColorBlack);

layer_add_child(window_get_root_layer(window), text_layer_get_layer(g_text_layer));
```

Now the explanation:

1. ``text_layer_create()`` - This creates the ``TextLayer`` and sets its frame
   to that in the ``GRect`` supplied as its only argument to x = 0, y = 0, width
   = 144 pixels and height = 168 pixels (this is the size of the entire screen).
   Just like ``window_create()``, this function returns a pointer to the newly
   created ``TextLayer``
2. ``text_layer_set_background_color()`` - This also does what it says: sets the
   ``TextLayer``'s background color to ``GColorClear``, which is transparent.
3. ``text_layer_set_text_color()`` - Similar to the last function, but sets the
   text color to ``GColorBlack``.
4. ``layer_add_child()`` - This is used to add the ``TextLayer``'s "root"
   ``Layer`` (which is the part drawn to the screen) as a 'child' of the
   ``Window``'s root layer. Simply put, all child ``Layer``s are drawn in front
   of their 'parents' and allows us to control layering of ``Layer``s and in
   which order they are drawn.

As should always be the case, we must add the required destruction function
calls to free up the memory we used in creating the ``TextLayer``. This is done
in the parent ``Window``'s `window_unload()` function, which now looks like
this:

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

Now for what we have been working towards all this time - making the app show
any text we want! After creating the ``TextLayer``, add a call to
``text_layer_set_text()`` to set the text it should display, such as the example
below:

```c
text_layer_set_text(g_text_layer, "Anything you want, as long as it is in quotes!");
```

Now you should be ready to see the fruits of your labor on your wrist! To do
this we must compile our C source code into a `.pbw` package file that the
Pebble app will install for us.

###Compilation and Installation

To do this, make sure you save your C file. Then go to the 'Compilation' screen
in the left pane and click 'Run build'. After the compiler returns 'Succeeded',
you can use either of the following options to install the app on your Pebble:

- Ensure you have enabled the
  [Pebble Developer Connection](/guides/tools-and-resources/developer-connection/)
  and enter the IP address shown into the 'Phone IP' box. Click 'Install and
  Run' to install your app.
- Use the 'Get PBW' button in your phone's browser to install via the Pebble app.

If your code does not compile, compare it to the
[sample code](https://gist.github.com/C-D-Lewis/a911f0b053260f209390) to see
where you might have made an error. Once this is successful, you should see
something similar to the screenshot below:

![Screenshot](/images/blog/tut_1_preview.png "Screenshot")

###Conclusions
There you have it, a *very* simple watchapp to show some text of your choosing.
This was done by:

1. Setting up boilerplate app structure.
2. Creating a ``Window`` with a child layer, in this case the ``TextLayer``.
3. Creating a ``TextLayer`` to show text on the screen.

By adding more types of layers such as ``BitmapLayer`` and `InverterLayer` we
create much more sophisticated apps. By extension with ``AppMessage``s and
``Clicks`` we can begin to respond to user data and input. All this and more to
come in future instalments!

###Source Code
The full source code file we have built up over the course of this article can
be found [here](https://gist.github.com/C-D-Lewis/a911f0b053260f209390) and
directly imported into CloudPebble
[as a new project](https://cloudpebble.net/ide/gist/a911f0b053260f209390). If
your code doesn't compile, have a look at this and see where you may have gone
wrong.

Best of luck, and let me know what you come up with! Send any feedback or
questions to [@PebbleDev](http://twitter.com/PebbleDev) or
[@Chris_DL](http://twitter.com/Chris_DL).


---
# 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 SDK Tutorial (Part 2)
author: Chris Lewis
excerpt: |
  This is the second in a series of articles aimed at helping developers new and
  experienced begin creating their own watchapps. In this section we will be
  using the TickTimerService to display the current time, which is the basis of
  all great watchfaces. After this, we will use GBitmaps and BitmapLayers to
  improve the aesthetics of our watchface.
---

##Your First Watchface

###Previous Tutorial Parts
[Pebble SDK Tutorial (Part 1): Your First Watchapp](/blog/2014/06/10/Pebble-SDK-Tutorial-1/)

###Introduction

This is the second in a series of articles aimed at helping developers new and
experienced begin creating their own watchapps. In this section we will be using
the ``TickTimerService`` to display the current time, which is the basis of all
great watchfaces. After this, we will use ``GBitmap``s and ``BitmapLayer``s to
improve the aesthetics of our watchface. By the end of this section, our app
will look like this:

![final](/images/blog/tut_2_frame_added.png "final")

###Getting Started

To begin creating this watchface, we will be using the
[source code](https://gist.github.com/C-D-Lewis/a911f0b053260f209390) from the
last post as a starting point, which you can import into
[CloudPebble]({{site.links.cloudpebble}}) as a new project
[here](https://cloudpebble.net/ide/gist/a911f0b053260f209390).

As you may have noticed, the current watchapp is started from the Pebble main
menu, and not on the watchface carousel like other watchfaces. To change this,
go to 'Settings' in your CloudPebble project and change the 'App Kind' to
'Watchface'. This will cause the app to behave in the same way as any other
watchface. If you were using the native SDK, this change would be done in the
`appinfo.json` file.

Once this is done, we will modify the main C file to prepare it for showing the
time. Remove the call to ``text_layer_set_text()`` to prevent the watchface
showing anything irrelevant before the time is shown. For reference,
`window_load()` should now look like this:

```c
void window_load(Window *window)
{
  //We will add the creation of the Window's elements here soon!
  g_text_layer = text_layer_create(GRect(0, 0, 144, 168));
  text_layer_set_background_color(g_text_layer, GColorClear);
  text_layer_set_text_color(g_text_layer, GColorBlack);

  layer_add_child(window_get_root_layer(window), text_layer_get_layer(g_text_layer));
}
```

###Telling the Time

Now we can use the ``TextLayer`` we created earlier to display the current time,
which is provided to us once we register a ``TickHandler`` with the system. The
first step to do this is to create an empty function in the format dictated by
the ``TickHandler`` documentation page. This is best placed before it will be
used, which is above `init()`:

```c
void tick_handler(struct tm *tick_time, TimeUnits units_changed)
{

}
```

This function is called by the system at a fixed tick rate depending on the
``TimeUnits`` value supplied when we register the handler. Let's do this now in
`init()` before ``window_stack_push()``, using the ``MINUTE_UNIT`` value to get
an update every minute change:

```c
tick_timer_service_subscribe(MINUTE_UNIT, (TickHandler)tick_handler);
```

Now that we have subscribed to the ``TickTimerService``, we can add code to the
`tick_handler()` function to use the time information provided in the
`tick_time` parameter to update the ``TextLayer``. The data stored within this
structure follows the
[ctime struct tm format](http://www.cplusplus.com/reference/ctime/tm/). This
means that we can access the current hour by using `tick_time->tm_hour` and the
current minute by using `tick_time->tm_min`, for example. Instead, we will use a
function called `strftime()` that uses the `tick_time` parameter to write a
time-formatted string into a buffer we provide, called `buffer`. Therefore the
new tick handler will look like this:

```c
void tick_handler(struct tm *tick_time, TimeUnits units_changed)
{
  //Allocate long-lived storage (required by TextLayer)
  static char buffer[] = "00:00";

  //Write the time to the buffer in a safe manner
  strftime(buffer, sizeof("00:00"), "%H:%M", tick_time);

  //Set the TextLayer to display the buffer
  text_layer_set_text(g_text_layer, buffer);
}
```

Now every time a minute passes `tick_handler()` will create a new string in the
buffer and display it in the ``TextLayer``. This is the basis of every
watchface. However, the text contained in the ``TextLayer`` is difficult to read
because the default system font is very samll, so we will change some of the
layout properties to better suit its purpose. Modify `window_load()` to add the
relevant ``TextLayer`` functions like so:

```c
void window_load(Window *window)
{
  //Create the TextLayer
  g_text_layer = text_layer_create(GRect(0, 59, 144, 50));
  text_layer_set_background_color(g_text_layer, GColorClear);
  text_layer_set_text_color(g_text_layer, GColorBlack);

  //Improve the layout to be more like a watchface
  text_layer_set_font(g_text_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
  text_layer_set_text_alignment(g_text_layer, GTextAlignmentCenter);

  layer_add_child(window_get_root_layer(window), text_layer_get_layer(g_text_layer));
}
```

Now the watchface should look more like a classic watchface with a larger font
and centered text:

![largetext](/images/blog/tut_2_largetext.png "large text")

###Adding Bitmaps
The next logical way to improve the watchface is to add some complementary
images. These take the form of ``GBitmap``s, and are referenced by a
`RESOURCE_ID` specified in CloudPebble Settings or `appinfo.json` when working
with the native SDK. The first bitmap we wil add will be a smart border for the
``TextLayer``, shown for you to use below:

![frame](/images/blog/tut_2_frame.png "frame")

To add this image to our project in CloudPebble, click "Add New" next to
Resources, navigate to your stored copy of the image above and give it an ID
such as "FRAME", then click Save.

Once this is done, we can add it to the watchface. The first step is to declare
two new global items: A ``GBitmap`` to hold the loaded image and a
``BitmapLayer`` to show it. Add these to the top of the file, alongside the
existing global variables:

```c
GBitmap *g_frame_bitmap;
BitmapLayer *g_frame_layer;
```

The next step is to allocate the ``GBitmap``, show it using the ``BitmapLayer``
and add it as a child of the main ``Window``. ``Layer``s are drawn in the order
they are added to their parent, so be sure to add the ``BitmapLayer`` **before**
the ``TextLayer``. This will ensure that the time is drawn on top of the image,
and not the other way around:

```c
//Create and add the image
g_frame_bitmap = gbitmap_create_with_resource(RESOURCE_ID_FRAME);
g_frame_layer = bitmap_layer_create(GRect(7, 56, 129, 60));
bitmap_layer_set_bitmap(g_frame_layer, g_frame_bitmap);
layer_add_child(window_get_root_layer(window), bitmap_layer_get_layer(g_frame_layer));
```

Once again, remember to add calls to `_destroy()` for each element to free
memory in `window_unload()`:

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

  gbitmap_destroy(g_frame_bitmap);
  bitmap_layer_destroy(g_frame_layer);
}
```

With these steps completed, a re-compile of your project should yield something
similar to that shown below:

![frameadded](/images/blog/tut_2_frame_added.png "frame added")

The ``TextLayer`` is now surrounded by a crisp frame border. It's not amazing,
but it's the start on a journey to a great watchface!

###Conclusion
There you have it: turning your simple watchapp into a basic watchface. In
summary:

1. Modify the App Kind to be a watchface instead of a watchapp.
2. Add a subscription to the ``TickTimerService`` to get the current time.
3. Modify the ``TextLayer`` layout to better suit the task of telling the time.
4. Add an image resource to the project
5. Display that image using a combination of ``GBitmap`` and ``BitmapLayer``.

From there you can add more images, change the text font and size and more to
further improve the look of the watchface. In future posts you will learn how to
use ``Timer``s and ``Animation``s to make it even better!

###Source Code
Just like last time, the full source code file can be found
[here](https://gist.github.com/C-D-Lewis/17eb11ab355950595ca2) and directly
imported into CloudPebble
[as a new project](https://cloudpebble.net/ide/gist/17eb11ab355950595ca2). If
your code doesn't compile, have a look at this and see where you may have gone
wrong.

Send any feedback or questions to [@PebbleDev](http://twitter.com/PebbleDev) or
[@Chris_DL](http://twitter.com/Chris_DL).


---
# 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: Getting Ready for Automatic App Updates
author: katharine
tags:
- Freshly Baked
---

We are pleased to announce that we will soon be automatically updating apps installed from the Pebble
appstore. We believe this will be a significant improvement to your ability to get your work into
the hands of your users, as well as to the user experience.

However, as developers, you need to make sure that your app is ready for these updates. In
particular, you will need to make sure that your app conforms to our new version numbering, and
that it can tolerate persistent storage left over from old app versions.

Version Numbers
---------------

To prepare for automatic app updates, we have imposed new requirements on the format of app
version numbers. The new format is:

major.minor

Where minor is optional, and each component is considered independently (so 2.10 is newer than 2.5).
For instance, all of the following are valid version codes:

* 1.0
* 2.3
* 0.1
* 2

However, neither of the following are:

* ~~1.0.0~~
* ~~2.5-beta6~~

We will automatically upgrade the app if the version number from the watch is less than the latest
released version on the appstore. If old versions of your app contained a "bugfix" number, we will
use a number truncated to the major.minor format — so "1.0.7" will be taken as "1.0".

Persistent Storage
------------------

Automatic updates will retain persistent storage between app versions. This ensures that your users
will not be frustrated by updates silently deleting their data and settings. However, this puts the
onus on you as developers to ensure your app behaves well when presented with an old version of
its persistent storage. If your app does not already use persistent storage, you can ignore this
section.

We recommend that you do this by versioning your persistent storage, and incrementing the version
every time its structure changes.

The easiest way to do this versioning will be to create a new key in your persistent storage
containing the app version. If you have not already versioned your storage, you should simply check
for the version key and assume its absence to mean "version zero".

Once you know the "storage version" of your app, you can perform some migration to make it match
your current format. If the version is too old, or you have multiple "version zero" formats that you
cannot otherwise distinguish, you may instead just delete the old keys - but keep in mind that this
is a poor user experience, and we recommend avoiding it wherever possible.

Ultimately, you might have code that looks something like this:

```c
#define STORAGE_VERSION_KEY 124 // any previously unused value
#define CURRENT_STORAGE_VERSION 3
// ...
static void clear_persist(void) {
	// persist_delete all your keys.
}

static void read_v2_persist(void) {
	// Read the old v2 format into some appropriate structure
}

static void read_v1_persist(void) {
	// Read the old v1 format into some appropriate structure.
}

static void migrate_persist(void) {
	uint32_t version = persist_read_int(STORAGE_VERSION_KEY); // defaults to 0 if key is missing.

	if(version > CURRENT_STORAGE_VERSION) {
		// This is more recent than what we expect; we can't know what happened, so delete it
		clear_persist();
	} else if(version == 2) {
		read_v2_persist();
		store_persist();
	} else if(version == 1) {
		read_v1_persist();
		store_persist();
	} else if(version == 0) {
		// Again, just delete this - perhaps we have multiple unversioned types, or 0 is just too
		// long ago to be worth handling.
		clear_persist();
	}
}
```

Obviously, that is just an example; you will have to deal with your own situation as appropriate.

If you have further questions about getting ready for automatic app updates, let us know! [Contact us](/contact) or
[post on our forums](https://forums.getpebble.com/categories/developer-discussion).