This page is outdated, intended for updating SDK 1.x apps to SDK 2.x. All app should now be created with SDK 3.x. For migrating an app from SDK 2.x to SDK 3.x, the read SDK 3.x Migration Guide.
This guide provides you with a summary and a detailed list of the changes, updates and new APIs available in Pebble SDK 2.0. To migrate your code successfully from Pebble SDK 1.x to Pebble SDK 2.x, you should read this guide.
In addition to updated and new Pebble APIs, you'll find updated developer tools and a simplified build system that makes it easier to create, build, and deploy Pebble apps.
Applications written for Pebble SDK 1.x do not work on Pebble 2.0. It is extremely important that you upgrade your apps, so that your users can continue to enjoy your watchfaces and watchapps.
These are the essential steps to perform the upgrade:
arm-cs-tools
. The version shipped with Pebble SDK 2
contains several important improvements that help reduce the size of the
binaries generated and improve the performance of your app.pip install --user -r /requirements.txt
).One of the new features introduced in Pebble native SDK 2.0 is the pebble
command
line tool. This tool is used to create new apps, build and install those apps on
your Pebble.
The tool was designed to simplify and optimize the build process for your Pebble watchfaces and watchapps. Give it a try right now:
$ pebble new-project helloworld
$ cd helloworld
$ ls
appinfo.json resources src wscript
Notice that the new SDK does not require symlinks as the earlier SDK did. There
is also a new appinfo.json
file, described in greater detail later in this
guide. The file provides you with a more readable format and includes all the
metadata about your app.
$ pebble build
...
Memory usage:
=============
Total app footprint in RAM: 801 bytes / ~24kb
Free RAM available (heap): 23775 bytes
[12/13] inject-metadata: build/pebble-app.raw.bin build/app_resources.pbpack.data -> build/pebble-app.bin
[13/13] helloworld.pbw: build/pebble-app.bin build/app_resources.pbpack -> build/helloworld.pbw
...
'build' finished successfully (0.562s)
You don't need to call the waf
tool to configure and then build the project
anymore (pebble
still uses waf
, however). The new SDK also gives you some
interesting information on how much memory your app will use and how much memory
will be left for you in RAM.
$ pebble install --phone 10.0.64.113 --logs
[INFO ] Installation successful
[INFO ] Enabling application logging...
[INFO ] Displaying logs ... Ctrl-C to interrupt.
[INFO ] D helloworld.c:58 Done initializing, pushed window: 0x2001a524
Installing an app with pebble
is extremely simple. It uses your phone and the
official Pebble application as a gateway. You do need to configure your phone
first, however. For more information on working with this tool, read
Command Line Tool.
You don't need to run a local HTTP server or connect with Bluetooth like you did with SDK 1.x. You will also get logs sent directly to the console, which will make development a lot easier!
Pebble 2.0 is a major release with many changes visible to users and developers and some major changes in the system that are not visible at first sight but will have a strong impact on your apps.
Here are the biggest changes in Pebble SDK 2.0 that will impact you when migrating your app. The changes are discussed in more detail below:
appinfo.json
, which includes your app name, UUID,
resources and a few other new configuration parameters. For more information,
refer to App Metadata.main()
and not pbl_main()
.This means that you'll have to change most of your system calls and significantly rework your app. This change was required to allow us to update the structs in the future (for example, to add new fields in them) without forcing you to recompile your app code.
To upgrade your app for Pebble SDK 2.0, you should first run the
pebble convert-project
command in your existing 1.x project. This will
automatically try to generate the appinfo.json
file based on your existing
source code and resource file. It will not touch your C code.
Please review your appinfo.json
file and make sure everything is OK. If it is,
you can safely remove the UUID and the PBL_APP_INFO
in your C file.
Refer to App Metadata for more information on application metadata and the basic structure of an app in Pebble SDK 2.0.
In Pebble SDK 1.x, you would reference Pebble header files with three include statements:
#include "pebble_os.h"
#include "pebble_app.h"
#include "pebble_fonts.h"
In Pebble SDK 2.x, you can replace them with one statement:
#include <pebble.h>
In Pebble SDK 1.x, your app was initialized in a pbl_main()
function:
void pbl_main(void *params) {
PebbleAppHandlers handlers = {
.init_handler = &handle_init
};
app_event_loop(params, &handlers);
}
In Pebble SDK 2.0:
pbl_main
is replaced by main
.PebbleAppHandlers
structure no longer exists. You call your init and
destroy handlers directly from the main()
function.int main(void) {
handle_init();
app_event_loop();
handle_deinit();
}
There were other fields in the PebbleAppHandlers
:
PebbleAppInputHandlers
:
Use a ClickConfigProvider
instead.PebbleAppMessagingInfo
: Refer to the section below on AppMessage
changes.PebbleAppTickInfo
: Refer to the section below on Tick events.PebbleAppTimerHandler
: Refer to the section below on Timer
events.PebbleAppRenderEventHandler
: Use a Layer
and call
layer_set_update_proc()
to provide your own function to render.In Pebble SDK 2.0, system structures are opaque and your app can't directly allocate memory for them. Instead, you use system functions that allocate memory and initialize the structure at the same time.
In Pebble SDK 1.x, you would allocate memory for system structures inside your app with static global variables. For example, it was very common to write:
Window my_window;
TextLayer text_layer;
void handle_init(AppContextRef ctx) {
window_init(&my_window, "My App");
text_layer_init(&text_layer, GRect(0, 0, 144, 20));
}
In Pebble SDK 2, you can't allocate memory statically in your program because
the compiler doesn't know at compile time how big the system structures are
(here, in the above code snippet Window
and TextLayer
). Instead, you use
pointers and ask the system to allocate the memory for you.
This simple example becomes:
Window *my_window;
TextLayer *text_layer;
void handle_init(void) {
my_window = window_create();
text_layer = text_layer_create(GRect(0, 0, 144, 20));
}
Instead of using *_init()
functions and passing them a pointer to the structure,
in SDK 2.0, you call functions that end in _create()
, and these functions will
allocate memory and return to your app a pointer to a structure that is
initialized.
Because the memory is dynamically allocated, it is extremely important that you
release that memory when you are finished using the structure. This can be done
with the *_destroy()
functions. For our example, we could write:
void handle_deinit(void) {
text_layer_destroy(text_layer);
window_destroy(my_window);
}
_init()
with their equivalent that end in
_create()
.*_destroy()
functions to release the memory.PebbleAppMessagingInfo
, you pass
them to app_message_open()
Instead of using a AppMessageCallbacksNode
structure and
app_message_register_callbacks()
, you register handler for the different
AppMessage
events with:
app_message_register_inbox_received()
app_message_register_inbox_dropped()
app_message_register_outbox_failed()
app_message_register_outbox_sent()
app_message_set_context(void *context)
: To set the context that will be
passed to all the handlers.app_message_out_get()
is replaced by app_message_outbox_begin()
.app_message_out_send()
is replaced by app_message_outbox_send()
.app_message_out_release()
is removed. You do not need to call this anymore.For more information, please review the AppMessage
API Documentation.
For working examples using AppMessage and AppSync in SDK 2.0, refer to:
/PebbleSDK-2.x/Examples/pebblekit-js/quotes
:
Demonstrates how to use PebbleKit JS to fetch price quotes from the web.
It uses AppMessage on the C side./PebbleSDK-2.x/Examples/pebblekit-js/weather
:
A PebbleKit JS version of the traditional weather-demo
example. It uses
AppSync on the C side.Callbacks for tick events can't be defined through PebbleAppHandlers
anymore.
Instead, use the Tick Timer Event service with:
tick_timer_service_subscribe()
.
For more information, read Event Services/
app_timer_send_event()
is replaced by app_timer_register()
.
For more information, refer to the Timer
API documentation.
PblTm
has been removed and replaced by the libc standard struct. Use struct
tm
from #include <time.h>
.
tm string_format_time()
function is replaced by strftime()
.
get_time()
is replaced by localtime(time(NULL))
. This lets you convert a
timestamp into a struct.
Pebble OS does not, as yet, support timezones. However, Pebble SDK 2
introduces gmtime()
and localtime()
functions to prepare for timezone
support.
In SDK 1.x, you would set up click handlers manually by modifying an array of config structures to contain the desired configuration. In SDK 2.x, how click handlers are registered and used has changed.
The following functions for subscribing to events have been added in SDK 2.x:
void window_set_click_context();
void window_single_click_subscribe();
void window_single_repeating_click_subscribe();
void window_multi_click_subscribe();
void window_multi_click_subscribe();
void window_long_click_subscribe();
void window_raw_click_subscribe();
For more information, refer to the Window
API documentation.
For example, in SDK 1.x you would do this:
void click_config_provider(ClickConfig **config, void *context) {
config[BUTTON_ID_UP]->click.handler = up_click_handler;
config[BUTTON_ID_UP]->context = context;
config[BUTTON_ID_UP]->click.repeat_interval_ms = 100;
config[BUTTON_ID_SELECT]->click.handler = select_click_handler;
config[BUTTON_ID_DOWN]->multi_click.handler = down_click_handler;
config[BUTTON_ID_DOWN]->multi_click.min = 2;
config[BUTTON_ID_DOWN]->multi_click.max = 10;
config[BUTTON_ID_DOWN]->multi_click.timeout = 0; /* default timeout */
config[BUTTON_ID_DOWN]->multi_click.last_click_only = true;
config[BUTTON_ID_SELECT]->long_click.delay_ms = 1000;
config[BUTTON_ID_SELECT]->long_click.handler = select_long_click_handler;
}
In SDK 2.x, you would use the following calls instead:
void click_config_provider(void *context) {
window_set_click_context(BUTTON_ID_UP, context);
window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, up_click_handler);
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_multi_click_subscribe(BUTTON_ID_DOWN, 2, 10, 0, true, down_click_handler);
window_long_click_subscribe(BUTTON_ID_SELECT, 1000, select_long_click_handler, NULL /* No handler on button release */);
}
Notice that the signature of ClickConfigProvider
has also changed. These
Clicks
API functions must be called from within the
ClickConfigProvider function. If they are not, your app code will fail.
graphics_text_draw()
has been renamed to graphics_draw_text()
, matching
the rest of Pebble's graphics_draw_ functions. There are no changes with the
usage of the function.Table 1. API changes from SDK 1.x to 2.x
API Call in SDK 1.x | API Call in SDK 2.x |
---|---|
#define APP_TIMER_INVALID_HANDLE ((AppTimerHandle)0) |
Changed. No longer needed; app_timer_register() always succeeds. See Timer . |
#define INT_MAX 32767 |
Changed. See #include <limits.h> |
AppTimerHandle app_timer_send_event(); |
See app_timer_register() for more information at Timer . |
ARRAY_MAX |
Removed from Pebble headers. Now use limits.h |
bool app_timer_cancel_event(); |
Changed. See app_timer_cancel() for more information at Timer . |
GContext *app_get_current_graphics_context(); |
Removed. Use the context supplied to you in the drawing callbacks. |
GSize text_layer_get_max_used_size(); |
Use text_layer_get_content_size() . See TextLayer . |
INT_MAX |
Removed from Pebble headers. Now use limits.h |
void get_time(); |
Use localtime(time(NULL)) from #include <time.h> . |
void resource_init_current_app(); |
No longer needed. |
void string_format_time(); |
Use strftime from #include <time.h> . |
void window_render(); |
No longer available. |
*_create()/*_destroy()
instead of *_init()/*_deinit()
functionsIf you were using the following _init()/_deinit()
functions, you should now
use *_create()/*_destroy()
instead when making these calls:
bool rotbmp_init_container();
See BitmapLayer
.bool rotbmp_pair_init_container();
BitmapLayer
.void action_bar_layer_init();
See ActionBarLayer
.void animation_init();
See Animation
.void bitmap_layer_init();
BitmapLayer
.void gbitmap_init_as_sub_bitmap();
See [Graphics Types.void gbitmap_init_with_data();
See Graphics Types.void inverter_layer_init();
Now InverterLayer
(deprecated in SDK 3.0).void layer_init();
See Layer
.void menu_layer_init();
See MenuLayer
.void number_window_init();
void property_animation_init_layer_frame();
See Animation
.void property_animation_init();
See Animation
.void rotbmp_deinit_container();
BitmapLayer
.void rotbmp_pair_deinit_container();
BitmapLayer
.void scroll_layer_init();
See ScrollLayer
.void simple_menu_layer_init();
See SimpleMenuLayer
.void text_layer_deinit();
See TextLayer
.void text_layer_init();
See TextLayer
.void window_deinit();
See Window
.void window_init();
See Window
.