mirror of
https://github.com/google/pebble.git
synced 2025-06-04 01:03:12 +00:00
Import the pebble dev site into devsite/
This commit is contained in:
parent
3b92768480
commit
527858cf4c
1359 changed files with 265431 additions and 0 deletions
333
devsite/source/_posts/2014-06-10-Pebble-SDK-Tutorial-1.md
Normal file
333
devsite/source/_posts/2014-06-10-Pebble-SDK-Tutorial-1.md
Normal file
|
@ -0,0 +1,333 @@
|
|||
---
|
||||
# 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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
###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).
|
Loading…
Add table
Add a link
Reference in a new issue