16 KiB
title | description | permalink | generate_toc | guide_group | order |
---|---|---|---|---|---|
SDK 2.x Migration Guide | Migrating Pebble apps from SDK 1.x to SDK 2.x. | /guides/migration/migration-guide/ | true | migration | 1 |
{% alert important %} 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 {% guide_link migration/migration-guide-3 %}. {% endalert %}
Introduction
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:
- You'll need to upgrade Pebble SDK on your computer, the firmware on your Pebble, and the Pebble mobile application on your phone.
- You need to upgrade the
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. - You need to upgrade the python dependencies
pip install --user -r {{ site.pb_sdk_path }}{{ site.pb_sdk2_package }}/requirements.txt
).
Discovering the new Pebble tools (Native SDK only)
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
{% guide_link tools-and-resources/pebble-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!
Upgrading a 1.x app to 2.0
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:
-
Every app now requires an
appinfo.json
, which includes your app name, UUID, resources and a few other new configuration parameters. For more information, refer to {% guide_link tools-and-resources/app-metadata %}. -
Your app entry point is called
main()
and notpbl_main()
. -
Most of the system structures are not visible to apps anymore, and instead of allocating the memory yourself, you ask the system to allocate the memory and return a pointer to the structure.
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.
-
Pebble has redesigned many APIs to follow standard C best practices and futureproof the SDK.
Application metadata
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 {% guide_link tools-and-resources/app-metadata %} for more information on application metadata and the basic structure of an app in Pebble SDK 2.0.
Pebble Header files
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>
Initializing your app
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 bymain
.- The
PebbleAppHandlers
structure no longer exists. You call your init and destroy handlers directly from themain()
function.
int main(void) {
handle_init();
app_event_loop();
handle_deinit();
}
There were other fields in the PebbleAppHandlers
:
PebbleAppInputHandlers
: Use aClickConfigProvider
instead.PebbleAppMessagingInfo
: Refer to the section below onAppMessage
changes.PebbleAppTickInfo
: Refer to the section below on Tick events.PebbleAppTimerHandler
: Refer to the section below onTimer
events.PebbleAppRenderEventHandler
: Use aLayer
and calllayer_set_update_proc()
to provide your own function to render.
Opaque structures and Dynamic Memory allocation
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.
Allocating dynamic memory: A simple example
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);
}
Dynamic memory: General rules in Pebble SDK 2.0
- Replace all statically allocated system structures with a pointer to the structure.
- Replace functions that ended in
_init()
with their equivalent that end in_create()
. - Keep pointers to the structures that you have initialized. Call the
*_destroy()
functions to release the memory.
AppMessage changes
-
Instead of defining your buffer sizes in
PebbleAppMessagingInfo
, you pass them toapp_message_open()
-
Instead of using a
AppMessageCallbacksNode
structure andapp_message_register_callbacks()
, you register handler for the differentAppMessage
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 byapp_message_outbox_begin()
. -
app_message_out_send()
is replaced byapp_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:
{{ site.pb_sdk2_package }}/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.{{ site.pb_sdk2_package }}/PebbleSDK-2.x/Examples/pebblekit-js/weather
: A PebbleKit JS version of the traditionalweather-demo
example. It uses AppSync on the C side.
Dealing with Tick events
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 {% guide_link events-and-services/events %}/
Timer changes
app_timer_send_event()
is replaced by app_timer_register()
.
For more information, refer to the Timer
API documentation.
WallTime API changes
-
PblTm
has been removed and replaced by the libc standard struct. Use structtm
from#include <time.h>
. -
tm string_format_time()
function is replaced bystrftime()
. -
get_time()
is replaced bylocaltime(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()
andlocaltime()
functions to prepare for timezone support.
Click handler changes
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.
Other changes
graphics_text_draw()
has been renamed tographics_draw_text()
, matching the rest of Pebble's graphics_draw_ functions. There are no changes with the usage of the function.
Quick reference for the upgrader
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. |
Using *_create()/*_destroy()
instead of *_init()/*_deinit()
functions
If you were using the following _init()/_deinit()
functions, you should now
use *_create()/*_destroy()
instead when making these calls:
bool rotbmp_init_container();
SeeBitmapLayer
.bool rotbmp_pair_init_container();
BitmapLayer
.void action_bar_layer_init();
SeeActionBarLayer
.void animation_init();
SeeAnimation
.void bitmap_layer_init();
BitmapLayer
.void gbitmap_init_as_sub_bitmap();
See [Graphics Types](Graphics Types
).void gbitmap_init_with_data();
See [Graphics Types](Graphics Types
).void inverter_layer_init();
NowInverterLayer
(deprecated in SDK 3.0).void layer_init();
SeeLayer
.void menu_layer_init();
SeeMenuLayer
.void number_window_init();
void property_animation_init_layer_frame();
SeeAnimation
.void property_animation_init();
SeeAnimation
.void rotbmp_deinit_container();
BitmapLayer
.void rotbmp_pair_deinit_container();
BitmapLayer
.void scroll_layer_init();
SeeScrollLayer
.void simple_menu_layer_init();
SeeSimpleMenuLayer
.void text_layer_deinit();
SeeTextLayer
.void text_layer_init();
SeeTextLayer
.void window_deinit();
SeeWindow
.void window_init();
SeeWindow
.