aboutsummaryrefslogtreecommitdiffstats
path: root/Makefile
Commit message (Expand)AuthorAgeFilesLines
* trivial changesMike Baker2006-11-281-1/+1
* clear .host.mk before running prereq checks (rechecks for missing packages li...Felix Fietkau2006-11-231-0/+3
* add prereq checks for target/linux/* and target/image/*, check for fdisk for ...Felix Fietkau2006-11-181-3/+11
* implement target profiles in menuconfigFelix Fietkau2006-11-121-2/+2
* tty detect fixFelix Fietkau2006-11-121-4/+1
* another optimizationFelix Fietkau2006-11-121-1/+6
* fix unnecessary rebuild of .targetinfoFelix Fietkau2006-11-121-2/+2
* fix typoFelix Fietkau2006-11-121-1/+1
* make target/linux/* directories self-contained, use the selected kernel versi...Felix Fietkau2006-11-111-25/+43
* another fix for invalid fd messagesFelix Fietkau2006-10-141-0/+3
* force LANG to CFelix Fietkau2006-10-141-0/+2
* fix invalid fd warning on mac os xFelix Fietkau2006-10-141-1/+1
* finally move buildroot-ng to trunkFelix Fietkau2016-03-201-0/+141
133'>133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
Movement: the community watch face app
======================================

The Sensor Watch Library allows you to write your own bare-metal applications for the Sensor Watch. This is great if you want full control over the code running on the device, but it also means that you may have to implement your own UI for many common tasks like setting the time or illuminating the screen.

**Movement** is an application that manages the display of different screens of content on the watch. These screens are called **watch faces**. Watch faces can be passive displays of information like a clock or a calendar, or they can be fully interactive user interfaces like the Preferences face, which allows the user to customize Movement's behavior. Movement handles the instantiation of your watch face and manages transitions between screens. It also provides a low-power sleep mode, triggered after a period of inactivity, to preserve the watch battery.

Several faces are provided that offer baseline functionality like a clock, a settings screen and an interface for setting the time. You can change and reorder the watch faces that Movement displays by editing `movement_config.h`, and you can write your own watch face using the guidance in this document.

Watch Face API
--------------

You can implement a watch face using just four functions: 

* `watch_face_setup`
* `watch_face_activate`
* `watch_face_loop`
* `watch_face_resign`

A fifth optional function, `watch_face_wants_background_task`, will be added to the guide at a later date. You may omit it.

To create a new watch face, you should create a new C header and source file in the watch-faces folder (i.e. for a watch face that displays moon phases: `moon_phase_face.h`, `moon_phase_face.c`), and implement these functions with your own unique prefix (i.e. `moon_phase_face_setup`). Then declare your watch face in your header file as follows:

```c
#define moon_phase_face ((const watch_face_t){ \
    moon_phase_face_setup, \
    moon_phase_face_activate, \
    moon_phase_face_loop, \
    moon_phase_face_resign, \
    NULL, /* or moon_phase_face_wants_background_task, if you implemented this function */ \
})
```

You will also have to add your watch face to the `Makefile` so that it will be compiled in, and to `movement_faces.h` so that it will be available to add to the carousel. A good example of the changes required [can be found here](https://github.com/joeycastillo/Sensor-Watch/commit/2a59ae950f653a1730686ede8f77d74aea125efe).

This section will go over how each function works. The section headings use the watch_face prefix, but know that you should implement each function with your own prefix as described above.

### watch_face_setup

If you have worked with Arduino, this function is similar to setup() in that it is called at first boot. In our case, it is also called when waking from sleep mode. You will be passed three parameters:

* `settings` - a pointer to the global Movement settings. You can use this to inform how you present your display to the user (i.e. taking into account whether they have silenced the buttons, or if they prefer 12 or 24-hour mode). You can also change these settings if you like.
* `position` - The 0-indexed position of your watch face in the list of faces.
* `context_ptr` - A pointer to a pointer. On first run, the pointee will be NULL. If you need to keep track of any state within your watch face, you should check if it is NULL, and if so, set its value to a pointer to some value or struct that will keep track of that state. For example, the Preferences face needs to keep track of which page the user is viewing (just an integer), whereas the Pulsometer face needs to track several different properties in a struct.

Beyond setting up the context pointer, you may want to configure any peripherals that your watch face requires; for example, a temperature watch face that reads a thermistor output may want to configure the ADC here. Still, to save power, you should avoid leaving the peripheral enabled, and wait to set pin function in the activate function.

It was mentioned above but it's worth mentioning again: this function will be called again after waking from sleep mode, since sleep mode disables all of the device's pins and peripherals. This would give the temperature watch face a chance to re-configure the ADC.

### watch_face_activate

This function is called just before your watch enters the foreground. If your watch face has any segments or text that is always displayed, you may want to set that here. In addition, if your watch face depends on data from a peripheral (like that temperature watch face), you will likely want to enable that peripheral and set any required pin modes here. This function is also passed a pointer to the settings and your application context.

### watch_face_loop

This is a lot like your loop() function in Arduinoland in that it is called repeatedly whenever your watch face is on screen. There is one crucial difference though: it is called less often. By default, this function is called once per second, and in response to events like button presses. You can request a more frequent tick interval by calling `movement_request_tick_frequency` with any power of 2 from 1 to 64. (there is a 128 Hz prescaler tick, but Movement reserves that for its own use)

In addition to the settings and context, this function receives another parameter: an `event`. This is a struct containing information about the event that triggered the update. You mostly need to check the `event_type` to determine what kind of event triggered the loop. A detailed list of all events is provided at the bottom of this document. 

There is also a `subsecond` property on the event that contains the fractional second of the event. If you are using 1 Hz updates, subsecond will always be 0.

You should set up a switch statement that handles, at the very least, the `EVENT_TICK` and `EVENT_MODE_BUTTON_UP` event types. The mode button up event occurs when the user presses the MODE button. **Your loop function SHOULD call the movement_move_to_next_face function in response to this event.** If you have a very good reason to override this behavior (e.g. your user interface requires all three buttons), you may do so, but the user will have to long-press the Mode button to advance to the next watch face.

Note that `watch_face_loop` returns a boolean value. This boolean value indicates to Movement whether the watch can enter standby mode after handling your loop (true), or whether it should stay awake (false). You SHOULD almost always return true here, as the watch uses significantly more power when idling as opposed to standing by. The only times you would return false here are if you are PWM'ing the LED or emitting a sound from the buzzer. Your watch face would want to keep the watch awake in this case because the PWM driver does not run in standby.

### watch_face_resign

This function is called just before your watch face goes off screen. You should disable any peripherals you enabled in `watch_face_activate`. The watch_face_resign function is passed the same settings and context as the other functions.

Putting it into practice: the Pulsometer watch face
---------------------------------------------------

Let's take a look at a watch face to see how these pieces fit together. A *pulsometer* is [a mechanical watch complication designed to determine someone's pulse](https://www.ablogtowatch.com/longines-pulsometer-chronograph-watch/) by counting their heartbeats: you start the pulsometer, count heartbeats, and stop it when you reach the specified number. The needle will point to the pulse rate.

Let's implement a pulsometer for the Sensor Watch. These files are in the repository as `pulsometer_face.h` and `pulsometer_face.c`, but we'll walk through them inline here. 

### pulsometer_face.h

First, let's take a look at the header file. First we include the Movement header file, which defines the various types we need to build a watch face:

```c
#include "movement.h"
```

The pulsometer needs to track certain state to do its job, so we define a struct to contain our watch face's context:

```c
typedef struct {
    bool measuring;
    int16_t pulse;
    int16_t ticks;
} pulsometer_state_t;
```

Finally, we define the four required functions, and define the watch face struct that users will use to add the face to their watch:

```c
void pulsometer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void pulsometer_face_activate(movement_settings_t *settings, void *context);
bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void pulsometer_face_resign(movement_settings_t *settings, void *context);

#define pulsometer_face ((const watch_face_t){ \
    pulsometer_face_setup, \
    pulsometer_face_activate, \
    pulsometer_face_loop, \
    pulsometer_face_resign, \
    NULL, \
})
```

### pulsometer_face.c

Now let's look at the implementation of the Pulsometer face. First up, we have a couple of definitions that we'll reference in the code: 

```c
#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.)
#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR)
```

These define the tick frequency: when the pulsometer widget is updating the screen, it will request 16 Hz updates (2^4).

#### Watch Face Setup

```c
void pulsometer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
    (void) settings;
    if (*context_ptr == NULL) *context_ptr = malloc(sizeof(pulsometer_state_t));
}
```

The `(void) settings;` line just silences a compiler warning about the unused parameter. The next line checks if the context pointer is NULL, and if so, allocates a `pulsometer_state_t`-sized chunk of memory to hold our state.

#### Watch Face Activation

```c
void pulsometer_face_activate(movement_settings_t *settings, void *context) {
    (void) settings;
    memset(context, 0, sizeof(pulsometer_state_t));
}
```

The pulsometer face doesn't need to keep track of context in between appearances; there's no need to keep displaying an old pulse reading hours or days after it was taken. So this line just sets the context to all zeroes before the watch face goes on screen.

#### Watch Face Loop

Next we have the loop function. First things first: it fetches our application context, and casts it to a `pulsometer_state_t` type so we can make use of it. It also creates a buffer for any text we plan to put on screen, and declares a switch statement for handling events:

```c
bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
    (void) settings;
    pulsometer_state_t *pulsometer_state = (pulsometer_state_t *)context;
    char buf[14];
    switch (event.event_type) {
```

Let's go through each case one by one. In response to the user releasing the MODE button, we tell Movement to move to the next watch face.

```c
case EVENT_MODE_BUTTON_UP:
    movement_move_to_next_face();
    break;
```

Similarly in response to the user pressing the LIGHT button, we tell Movement to illuminate the LED. Movement does not do this automatically, in case your watch face UI has another use for the LIGHT button.

```c
case EVENT_LIGHT_BUTTON_DOWN:
    movement_illuminate_led();
    break;
```

The ALARM button is the main button the user will use to interact with the pulsometer. In response to the user pressing the ALARM button, we begin a measurement. We also request a faster tick frequency, so that we can update the display at 16 Hz.

```c
case EVENT_ALARM_BUTTON_DOWN:
    pulsometer_state->measuring = true;
    pulsometer_state->pulse = 0xFFFF;
    pulsometer_state->ticks = 0;
    movement_request_tick_frequency(PULSOMETER_FACE_FREQUENCY);
    break;
```

When the user releases the ALARM button, we finish the measurement. We also scale the update frequency back down to 1 Hz.

```c
case EVENT_ALARM_BUTTON_UP:
case EVENT_ALARM_LONG_PRESS:
    pulsometer_state->measuring = false;