Browse Source

Initial commit of C watchface tutorial

master
T. Meissner 9 years ago
commit
a8cb56b85c
7 changed files with 472 additions and 0 deletions
  1. +6
    -0
      create_a_c_watchface/.gitignore
  2. +54
    -0
      create_a_c_watchface/appinfo.json
  3. BIN
      create_a_c_watchface/resources/fonts/perfect-dos-vga.ttf
  4. BIN
      create_a_c_watchface/resources/images/background.png
  5. +94
    -0
      create_a_c_watchface/src/js/pebble-js-app.js
  6. +277
    -0
      create_a_c_watchface/src/main.c
  7. +41
    -0
      create_a_c_watchface/wscript

+ 6
- 0
create_a_c_watchface/.gitignore View File

@ -0,0 +1,6 @@
# Ignore build generated files
build
# Ignore waf lock file
.lock-waf*

+ 54
- 0
create_a_c_watchface/appinfo.json View File

@ -0,0 +1,54 @@
{
"uuid": "3c6e0682-a917-4283-a27e-349f2de629fa",
"shortName": "create_a_c_watchface",
"longName": "create_a_c_watchface",
"companyName": "goodcleanfun",
"versionLabel": "1.0",
"sdkVersion": "3",
"targetPlatforms": ["aplite", "basalt"],
"watchapp": {
"watchface": true
},
"appKeys": {
"dummy": 0
},
"capabilities":
[ "location" ],
"appKeys": {
"KEY_TEMPERATURE": 0,
"KEY_CONDITIONS": 1
},
"resources": {
"media": [
{
"type": "font",
"name": "FONT_PERFECT_DOS_16",
"file": "fonts/perfect-dos-vga.ttf",
"compatibility":"2.7"
},
{
"type": "font",
"name": "FONT_PERFECT_DOS_20",
"file": "fonts/perfect-dos-vga.ttf",
"compatibility":"2.7"
},
{
"type": "font",
"name": "FONT_PERFECT_DOS_24",
"file": "fonts/perfect-dos-vga.ttf",
"compatibility":"2.7"
},
{
"type": "font",
"name": "FONT_PERFECT_DOS_48",
"file": "fonts/perfect-dos-vga.ttf",
"compatibility":"2.7"
},
{
"type": "png",
"name": "TUTORIAL_BACKGROUND",
"file": "images/background.png"
}
]
}
}

BIN
create_a_c_watchface/resources/fonts/perfect-dos-vga.ttf View File


BIN
create_a_c_watchface/resources/images/background.png View File

Before After
Width: 144  |  Height: 168  |  Size: 33 KiB

+ 94
- 0
create_a_c_watchface/src/js/pebble-js-app.js View File

@ -0,0 +1,94 @@
var xhrRequest = function (url, type, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
callback(this.responseText);
};
xhr.open(type, url);
xhr.send();
};
function locationSuccess(pos) {
// generic API key, has to be replaced by developers one
var api_key = 'bd82977b86bf27fb59a04b61b657fb6f';
// Construct URL
var url = 'http://api.openweathermap.org/data/2.5/weather?lat=' +
pos.coords.latitude + '&lon=' + pos.coords.longitude +
'&APPID=' + api_key;
// Send request to OpenWeatherMap
xhrRequest(url, 'GET',
function(responseText) {
// responseText contains a JSON object with weather info
var json = JSON.parse(responseText);
// Temperature in Kelvin requires adjustment
var temperature = Math.round(json.main.temp - 273.15);
console.log('Temperature is ' + temperature);
// Conditions
var conditions = json.weather[0].main;
console.log('Conditions are ' + conditions);
// Assemble dictionary using our keys
var dictionary = {
'KEY_TEMPERATURE': temperature,
'KEY_CONDITIONS': conditions
};
// Send to Pebble
Pebble.sendAppMessage(dictionary,
function(e) {
console.log('Weather info sent to Pebble successfully!');
},
function(e) {
console.log('Error sending weather info to Pebble!');
}
);
}
);
}
function locationError(err) {
console.log('Error requesting location!');
}
function getWeather() {
navigator.geolocation.getCurrentPosition(
locationSuccess,
locationError,
{timeout: 15000, maximumAge: 60000}
);
}
// Listen for when the watchface is opened
Pebble.addEventListener('ready',
function(e) {
console.log('PebbleKit JS ready!');
// Get the initial weather
getWeather();
}
);
// Listen for when an AppMessage is received
Pebble.addEventListener('appmessage',
function(e) {
console.log('AppMessage received!');
getWeather();
}
);

+ 277
- 0
create_a_c_watchface/src/main.c View File

@ -0,0 +1,277 @@
#include <pebble.h>
#define KEY_TEMPERATURE 0
#define KEY_CONDITIONS 1
// Function signatures
static void update_time();
static void tick_handler(struct tm *tick_time, TimeUnits units_changed);
static void main_window_load(Window *window);
static void main_window_unload(Window *window);
static void init();
static void deinit();
static void inbox_received_callback(DictionaryIterator *iterator, void *context);
static void inbox_dropped_callback(AppMessageResult reason, void *context);
static void outbox_failed_callback(DictionaryIterator *iterator, AppMessageResult reason, void *context);
static void outbox_sent_callback(DictionaryIterator *iterator, void *context);
// Global variables
static Window *s_main_window;
static TextLayer *s_time_layer;
static TextLayer *s_weather_layer;
static TextLayer *s_daymonth_layer;
//static TextLayer *s_year_layer;
static GFont s_time_font;
static GFont s_weather_font;
static GFont s_date_font;
static BitmapLayer *s_background_layer;
static GBitmap *s_background_bitmap;
int main(void) {
init();
app_event_loop();
deinit();
}
static void update_time() {
// Get a tm structure
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);
// Create a long-lived buffer
static char buffer[] = "00:00";
// Write the current hours and minutes into the buffer
if (clock_is_24h_style()) {
// Use 24 hour format
strftime(buffer, sizeof("00:00"), "%H:%M", tick_time);
} else {
// Use 12 hour format
strftime(buffer, sizeof("00:00"), "%I:%M", tick_time);
}
// Display this time on the TextLayer
text_layer_set_text(s_time_layer, buffer);
}
static void update_date() {
// Get a tm structure
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);
// Create a long-lived buffer
static char buffer_daymonth[] = "00.000";
// Write the current hours and minutes into the buffer
strftime(buffer_daymonth, sizeof(buffer_daymonth), "%e.%b", tick_time);
// Display this time on the TextLayer
text_layer_set_text(s_daymonth_layer, buffer_daymonth);
}
static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
update_time();
if (units_changed & DAY_UNIT) {
update_date();
}
// Get weather update every 30 minutes
if (tick_time->tm_min % 30 == 0) {
// Begin dictionary
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
// Add a key-value pair
dict_write_uint8(iter, 0, 0);
// Send the message!
app_message_outbox_send();
}
}
static void inbox_received_callback(DictionaryIterator *iterator, void *context) {
// Store incoming information
static char temperature_buffer[8];
static char conditions_buffer[32];
static char weather_layer_buffer[32];
// Read first item
Tuple *t = dict_read_first(iterator);
// For all items
while(t != NULL) {
// Which key was received?
switch(t->key) {
case KEY_TEMPERATURE:
snprintf(temperature_buffer, sizeof(temperature_buffer), "%d°C", (int)t->value->int32);
break;
case KEY_CONDITIONS:
snprintf(conditions_buffer, sizeof(conditions_buffer), "%s", t->value->cstring);
break;
default:
APP_LOG(APP_LOG_LEVEL_ERROR, "Key %d not recognized!", (int)t->key);
break;
}
// Look for next item
t = dict_read_next(iterator);
}
// Assemble full string and display
snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%s, %s", temperature_buffer, conditions_buffer);
text_layer_set_text(s_weather_layer, weather_layer_buffer);
}
static void inbox_dropped_callback(AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Message dropped!");
}
static void outbox_failed_callback(DictionaryIterator *iterator, AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Outbox send failed!");
}
static void outbox_sent_callback(DictionaryIterator *iterator, void *context) {
APP_LOG(APP_LOG_LEVEL_INFO, "Outbox send success!");
}
static void main_window_load(Window *window) {
// Create GBitmap, then set to created BitmapLayer
s_background_bitmap = gbitmap_create_with_resource(RESOURCE_ID_TUTORIAL_BACKGROUND);
s_background_layer = bitmap_layer_create(GRect(0, 0, 144, 168));
bitmap_layer_set_bitmap(s_background_layer, s_background_bitmap);
// Create time TextLayer
s_time_layer = text_layer_create(GRect(5, 52, 139, 50));
text_layer_set_background_color(s_time_layer, GColorClear);
text_layer_set_text_color(s_time_layer, GColorBlack);
// Create temperature Layer
s_weather_layer = text_layer_create(GRect(0, 130, 144, 25));
text_layer_set_background_color(s_weather_layer, GColorClear);
text_layer_set_text_color(s_weather_layer, GColorWhite);
text_layer_set_text(s_weather_layer, "Loading...");
// Create day-month TextLayer
s_daymonth_layer = text_layer_create(GRect(5, 12, 139, 30));
text_layer_set_background_color(s_daymonth_layer, GColorBlack);
text_layer_set_text_color(s_daymonth_layer, GColorWhite);
// Create year TextLayer
// s_year_layer = text_layer_create(GRect(5, 123, 139, 30));
// text_layer_set_background_color(s_year_layer, GColorBlack);
// text_layer_set_text_color(s_year_layer, GColorWhite);
// Create GFont
s_time_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_PERFECT_DOS_48));
s_weather_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_PERFECT_DOS_16));
s_date_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_PERFECT_DOS_24));
// Apply to TextLayer
text_layer_set_font(s_time_layer, s_time_font);
text_layer_set_font(s_weather_layer, s_weather_font);
text_layer_set_font(s_daymonth_layer, s_date_font);
// text_layer_set_font(s_year_layer, s_date_font);
// Improve the layout to be more like a watchface
text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);
text_layer_set_text_alignment(s_weather_layer, GTextAlignmentCenter);
text_layer_set_text_alignment(s_daymonth_layer, GTextAlignmentCenter);
// text_layer_set_text_alignment(s_year_layer, GTextAlignmentCenter);
// Add it as a child layer to the Window's root layer
layer_add_child(window_get_root_layer(window), bitmap_layer_get_layer(s_background_layer));
layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_time_layer));
layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_weather_layer));
layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_daymonth_layer));
// layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_year_layer));
}
static void main_window_unload(Window *window) {
// Destroy TextLayer
text_layer_destroy(s_time_layer);
text_layer_destroy(s_weather_layer);
text_layer_destroy(s_daymonth_layer);
// text_layer_destroy(s_year_layer);
// Unload GFont
fonts_unload_custom_font(s_time_font);
fonts_unload_custom_font(s_weather_font);
fonts_unload_custom_font(s_date_font);
// Destroy GBitmap
gbitmap_destroy(s_background_bitmap);
// Destroy BitmapLayer
bitmap_layer_destroy(s_background_layer);
}
static void init() {
// Create main Window element and assign to pointer
s_main_window = window_create();
// Set handlers to manage the elements inside the Window
window_set_window_handlers(s_main_window, (WindowHandlers){
.load = main_window_load,
.unload = main_window_unload
});
// Show the Window on the watch, with animated=true
window_stack_push(s_main_window, true);
// Register with TickTimerService
tick_timer_service_subscribe((MINUTE_UNIT | DAY_UNIT), tick_handler);
// Register AppMessage callbacks
app_message_register_inbox_received(inbox_received_callback);
app_message_register_inbox_dropped(inbox_dropped_callback);
app_message_register_outbox_failed(outbox_failed_callback);
app_message_register_outbox_sent(outbox_sent_callback);
// Open AppMessage
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
update_time();
update_date();
}
static void deinit() {
// Destroy Window
window_destroy(s_main_window);
}

+ 41
- 0
create_a_c_watchface/wscript View File

@ -0,0 +1,41 @@
#
# This file is the default set of rules to compile a Pebble project.
#
# Feel free to customize this to your needs.
#
import os.path
top = '.'
out = 'build'
def options(ctx):
ctx.load('pebble_sdk')
def configure(ctx):
ctx.load('pebble_sdk')
def build(ctx):
ctx.load('pebble_sdk')
build_worker = os.path.exists('worker_src')
binaries = []
for p in ctx.env.TARGET_PLATFORMS:
ctx.set_env(ctx.all_envs[p])
ctx.set_group(ctx.env.PLATFORM_NAME)
app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'),
target=app_elf)
if build_worker:
worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf})
ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'),
target=worker_elf)
else:
binaries.append({'platform': p, 'app_elf': app_elf})
ctx.set_group('bundle')
ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js'))

Loading…
Cancel
Save