pebble/devsite/source/_posts/2014-06-10-Pebble-SDK-Tutorial-1.md

334 lines
12 KiB
Markdown
Raw Permalink Normal View History

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