Когда мне привезли часы Pebble, я думал это просто умные часы. Ну, там смс на экранчике показать, время в двух поясах, поставить вместо цифровых — хипстерские аналоговые. И так далее.
Но оказывается, у часов есть довольно большое комьюнити, открытое API для создания своих приложений, среда онлайн-разработки — в общем, идеальная игрушка для скучающего разработчика.
Итак, что у нас есть? Три кнопки справа, одна кнопка слева, экран разрешением 144х168 пикселей.
Два типа программ — watchface, скины для часов и watchapp — приложения для часов. Первые не могут реагировать на кнопки, вторые могут. Первые перелистываются кнопками вверх и вниз в режиме отображения часов, вторые — показываются в конце меню после нажатия средней кнопки. У приложений по умолчанию есть верхний бар, в котором при запуске отображается название приложения, а во время работы — часы, но его можно отключить. Все внутренности полностью одинаковы, можно поменять тип приложения пересборкой, не внося изменений в код.
Начинаем
Заходим на cloudpebble.net, создаем аккаунт, нажимаем Create Project, выбираем SDK 2.
Сбоку видим:
Settings — настройки проекта
Compilation — сборка проекта
main.c — наш единственный файл проекта
Заходим в Settings.
App kind — это как раз тип, скин для часов или приложение.
Short Name отображается в меню часов, Long name — в приложении на телефоне(менюшка, из которой можно удалять приложения)
Menu image — картинка в меню часов.
UUID — идентификатор часов. Используется, например, при обновлении приложения. Если у загружаемого приложения такой же номер, как у одного из старых, оно будет заменено на новое.
В разделе Compilation можно собрать программу(кнопка Run build), после чего вам дадут ссылку на бинарник и QR-код для скачивания оного. Его можно сосканировать телефоном, и сразу же установить на часы. Делать это придется часто…
Теперь открываем main.c, и копируем вместо того, что там есть, вот такой код:
Сынок, это наша библия… Нет батя, это наша камасутра.
Приведенная выше программа не делает ничего полезного, просто показывает пустой экран. Не реагирует на кнопки, при нажатии кнопки назад — завершается.
Выведем какую-нибудь надпись. Для начала, установим фоновый цвет окна программы:
Второй параметр может принимать значения: GColorBlack — черный цвет, GColorWhite — белый цвет, GColorClear —прозрачная краска прозрачный фон, через который будет виден предыдущий слой(если бы он был). По умолчанию он равен White, вот почему у первой программы был белый фон. Само-собой, это надо делать уже после функции window_create.
Для того, чтобы вывести текст, нам потребуется текстовый слой.
Создаем слой:
Инициализируем и задаем координаты:
GRect — функция создания прямоугольника. Координаты она получает в следующем порядке — первые два числа это координаты верхнего левого угла треугольника — x, y. Вторые два числа — это ширина и высота нашего прямоугольника. Для удобства сделал вот такую картинку:
Помните, что координаты отсчитываются от физического начала экрана, но в режиме приложения первые 16 пикселей закроет верхний бар, если его не убрать.
Вот картинка в размере 1:1:
Открыв ее в любом редакторе очень удобно рассчитывать координаты. К сожалению, среда разработки такой возможности не предоставляет.
По-умолчанию, цвет шрифта — черный, цвет фона — белый, выравнивание по левому краю, шрифт — Raster Gothic 14.
Меняем все это. Фон у нас черный, значит цвет букв должен быть белым:
Фон(текстового слоя) делаем прозрачным:
Выравнивание — по центру:
А шрифт делаем чуть больше:
Делаем ребенка Добавляем созданный нами слой к слою главного экрана в качестве дочернего:
И наконец записываем в слой наш текст:
А при выходе — не забываем освобождать память:
Наконец, можно добавить в наши часы отображение времени.
Создаем переменную нужного типа(время в секундах) и кладем в нее текущее время в формате POSIX
Так как нам нужно только время, а в переменной у нас валяется вся информация до кучи, надо выделить нужные нам части. Делается это функцией strftime. В качестве аргументов она принимает следующие переменные: переменная записи результата, ее размер, формат записи, переменная в которой находится время. Так как функции нужна переменная(она не может писать в текстовый слой), и ее размер, надо сначала создать такую переменную.
Конечно, не обязательно писать при инициализации 00:00:00, можно и «ХХХХХХХХ», а можно и ничего не писать, а просто задать размер во втором аргументе функции числом, но это будет не очень понятно.
Если в current_time у нас всегда актуальное время, то начиная с этого момента, в переменной time остается форматированое значение времени на момент выполнения strftime. То, как оно форматируется — зависит от третьего аргумента — %T. Документация на формат легко гуглится по названию функции, например вот. В данном случае %T — это тоже самое что и "%H:%M:%S". Часы, минуты и секунды, разделенные двоеточиями.
Теперь можно записать это значение в текстовый слой:
Вроде как у нас получились часы. И при запуске они покажут правильное время. Вот только проблема — они не обновляются, и показывают правильное время только один раз в сутки.
Нам надо как-то каждую секунду пересчитывать время и выводить его на экран. Делается это вот как.
Создаем функцию, которая будет это делать, и переносим туда все, что касается пересчета и вывода времени:
Подписываемся на сервис таймера:
Первый аргумент означает, что функция во втором аргументе будет вызываться каждую секунду. Если в часах не будет секунд, то можно экономить энергию, и будить процессор в 60 раз меньше, вызывая функцию каждую минуту: MINUTE_UNIT
Или каждый час: HOUR_UNIT
Или каждый день: DAY_UNIT
Ну, вы поняли логику: MONTH_UNIT, YEAR_UNIT
После этого желательно вызвать эту функцию принудительно, не дожидаясь очередного тика таймера — ну просто для красоты, чтобы секунду не смотреть на пустой экран. Это хорошо если секунду. А если таймер каждую минуту тикает?
В итоге наши часы выглядят вот так:
Исходник:
Ссылки:
Httpeble: библиотека, реализующая http-запросы внутри программ
[EN]Проект по-настоящему умных часов(календарь, погода, напоминания, поиск телефона, HTTP-запросы для управления умным домом, управление музыкой, управление камерой, GPS, биржевые сводки)
PapaBubaDiop пишет о разработке игры под Pebble.
Огромная коллекция различных приложений для pebble, большинство — с исходниками.
Но оказывается, у часов есть довольно большое комьюнити, открытое API для создания своих приложений, среда онлайн-разработки — в общем, идеальная игрушка для скучающего разработчика.
Итак, что у нас есть? Три кнопки справа, одна кнопка слева, экран разрешением 144х168 пикселей.
Два типа программ — watchface, скины для часов и watchapp — приложения для часов. Первые не могут реагировать на кнопки, вторые могут. Первые перелистываются кнопками вверх и вниз в режиме отображения часов, вторые — показываются в конце меню после нажатия средней кнопки. У приложений по умолчанию есть верхний бар, в котором при запуске отображается название приложения, а во время работы — часы, но его можно отключить. Все внутренности полностью одинаковы, можно поменять тип приложения пересборкой, не внося изменений в код.
Начинаем
Заходим на cloudpebble.net, создаем аккаунт, нажимаем Create Project, выбираем SDK 2.
Сбоку видим:
Settings — настройки проекта
Compilation — сборка проекта
main.c — наш единственный файл проекта
Заходим в Settings.
App kind — это как раз тип, скин для часов или приложение.
Short Name отображается в меню часов, Long name — в приложении на телефоне(менюшка, из которой можно удалять приложения)
Menu image — картинка в меню часов.
UUID — идентификатор часов. Используется, например, при обновлении приложения. Если у загружаемого приложения такой же номер, как у одного из старых, оно будет заменено на новое.
В разделе Compilation можно собрать программу(кнопка Run build), после чего вам дадут ссылку на бинарник и QR-код для скачивания оного. Его можно сосканировать телефоном, и сразу же установить на часы. Делать это придется часто…
Теперь открываем main.c, и копируем вместо того, что там есть, вот такой код:
#include "pebble.h"
Window *window; /*создаем окно по имени... окно! человек человеку волк, а зомби зомби зомби. */
int main(void) {
window = window_create(); /* выделяем память и создаем окно с параметрами по умолчанию http://goo.gl/mdb5B9*/
window_stack_push(window, true); /* настраиваем анимацию при открытии этого окна. Так как оно у нас единственное - анимания будет показываться при переходе из меню в приложение. http://goo.gl/jXG3aw */
app_event_loop(); /* ждем действий в бесконечном цикле http://goo.gl/7hLyRX */
window_destroy(window); /* очищаем память и уничтожаем окно http://goo.gl/G3FJ6i */
}
После описания — ссылки на страницы API по той или иной функции. Приведенная выше программа не делает ничего полезного, просто показывает пустой экран. Не реагирует на кнопки, при нажатии кнопки назад — завершается.
Выведем какую-нибудь надпись. Для начала, установим фоновый цвет окна программы:
window_set_background_color(window, GColorBlack);
Второй параметр может принимать значения: GColorBlack — черный цвет, GColorWhite — белый цвет, GColorClear —
Для того, чтобы вывести текст, нам потребуется текстовый слой.
Создаем слой:
TextLayer *text_layer;
Инициализируем и задаем координаты:
text_layer = text_layer_create(GRect(0, 0, 144, 168));
GRect — функция создания прямоугольника. Координаты она получает в следующем порядке — первые два числа это координаты верхнего левого угла треугольника — x, y. Вторые два числа — это ширина и высота нашего прямоугольника. Для удобства сделал вот такую картинку:
Помните, что координаты отсчитываются от физического начала экрана, но в режиме приложения первые 16 пикселей закроет верхний бар, если его не убрать.
Вот картинка в размере 1:1:
Открыв ее в любом редакторе очень удобно рассчитывать координаты. К сожалению, среда разработки такой возможности не предоставляет.
По-умолчанию, цвет шрифта — черный, цвет фона — белый, выравнивание по левому краю, шрифт — Raster Gothic 14.
Меняем все это. Фон у нас черный, значит цвет букв должен быть белым:
text_layer_set_text_color(text_layer, GColorWhite);
Фон(текстового слоя) делаем прозрачным:
text_layer_set_background_color(text_layer, GColorClear);
Выравнивание — по центру:
text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
А шрифт делаем чуть больше:
text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD));
Вот список всех стандартных шрифтов
FONT_KEY_GOTHIC_14
FONT_KEY_GOTHIC_14_BOLD
FONT_KEY_GOTHIC_18
FONT_KEY_GOTHIC_18_BOLD
FONT_KEY_GOTHIC_24
FONT_KEY_GOTHIC_24_BOLD
FONT_KEY_GOTHIC_28
FONT_KEY_GOTHIC_28_BOLD
FONT_KEY_BITHAM_30_BLACK
FONT_KEY_BITHAM_42_BOLD
FONT_KEY_BITHAM_42_LIGHT
FONT_KEY_BITHAM_42_MEDIUM_NUMBERS
FONT_KEY_BITHAM_34_MEDIUM_NUMBERS
FONT_KEY_BITHAM_34_LIGHT_SUBSET
FONT_KEY_BITHAM_18_LIGHT_SUBSET
FONT_KEY_ROBOTO_CONDENSED_21
FONT_KEY_ROBOTO_BOLD_SUBSET_49
FONT_KEY_DROID_SERIF_28_BOLD
FONT_KEY_GOTHIC_14_BOLD
FONT_KEY_GOTHIC_18
FONT_KEY_GOTHIC_18_BOLD
FONT_KEY_GOTHIC_24
FONT_KEY_GOTHIC_24_BOLD
FONT_KEY_GOTHIC_28
FONT_KEY_GOTHIC_28_BOLD
FONT_KEY_BITHAM_30_BLACK
FONT_KEY_BITHAM_42_BOLD
FONT_KEY_BITHAM_42_LIGHT
FONT_KEY_BITHAM_42_MEDIUM_NUMBERS
FONT_KEY_BITHAM_34_MEDIUM_NUMBERS
FONT_KEY_BITHAM_34_LIGHT_SUBSET
FONT_KEY_BITHAM_18_LIGHT_SUBSET
FONT_KEY_ROBOTO_CONDENSED_21
FONT_KEY_ROBOTO_BOLD_SUBSET_49
FONT_KEY_DROID_SERIF_28_BOLD
layer_add_child(window_get_root_layer(window), text_layer_get_layer(text_layer));
И наконец записываем в слой наш текст:
text_layer_set_text(text_layer, "Hi, habrahabr!");
А при выходе — не забываем освобождать память:
text_layer_destroy(text_layer);
Наша программа уже выглядить вот так:А вот ее исходник. Можно скопировать в редактор, собрать и запустить на часах, чтобы похвастать: я написал программу для часов(!)
#include "pebble.h"
Window *window;
TextLayer *text_layer; /* создаем текстовый слой по имени... ну вы поняли */
int main(void) {
window = window_create();
window_set_background_color(window, GColorBlack); /* http://goo.gl/B6tj94 */
window_stack_push(window, true);
text_layer = text_layer_create(GRect(0, 0, 144, 168)); /* создаем прямоугольник (http://goo.gl/00eAW6) и
кидаем указателем в функцию создания слоя http://goo.gl/kYuFh5 */
text_layer_set_text_color(text_layer, GColorWhite); /* устанавливаем цвет шрифта http://goo.gl/wt4ZIC */
text_layer_set_background_color(text_layer, GColorClear); /* устанавливаем цвет фона слоя http://goo.gl/Y7HARg */
text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); /* устанавливаем шрифт
http://goo.gl/MOhxNe */
text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); /* Выравниваем по центру http://goo.gl/K6LWeG */
layer_add_child(window_get_root_layer(window), text_layer_get_layer(text_layer)); /* Делаем слой дочерним и помещаем его перед слоем окна http://goo.gl/jFy8LV */
text_layer_set_text(text_layer, "Hi, habrahabr!"); /* Выводим текст http://goo.gl/KT1hD6 */
app_event_loop();
text_layer_destroy(text_layer);
window_destroy(window);
}
Наконец, можно добавить в наши часы отображение времени.
Создаем переменную нужного типа(время в секундах) и кладем в нее текущее время в формате POSIX
time_t now = time(NULL);
Создаем структуру, которая будет хранить значение текущего времени в более читабельном формате — год, месяц, день, час, минута, секунда, а так же день в недели, день в году, и AM/PM: struct tm *current_time = localtime(&now);
А после этого записываем в нее время, конвертированное функцией localtime из POSIX в читабельный вид.Так как нам нужно только время, а в переменной у нас валяется вся информация до кучи, надо выделить нужные нам части. Делается это функцией strftime. В качестве аргументов она принимает следующие переменные: переменная записи результата, ее размер, формат записи, переменная в которой находится время. Так как функции нужна переменная(она не может писать в текстовый слой), и ее размер, надо сначала создать такую переменную.
static char time[] = "00:00:00";
Конечно, не обязательно писать при инициализации 00:00:00, можно и «ХХХХХХХХ», а можно и ничего не писать, а просто задать размер во втором аргументе функции числом, но это будет не очень понятно.
strftime(time, sizeof(time), "%T", current_time);
Если в current_time у нас всегда актуальное время, то начиная с этого момента, в переменной time остается форматированое значение времени на момент выполнения strftime. То, как оно форматируется — зависит от третьего аргумента — %T. Документация на формат легко гуглится по названию функции, например вот. В данном случае %T — это тоже самое что и "%H:%M:%S". Часы, минуты и секунды, разделенные двоеточиями.
Теперь можно записать это значение в текстовый слой:
text_layer_set_text(text_layer, time);
Вроде как у нас получились часы. И при запуске они покажут правильное время. Вот только проблема — они не обновляются, и показывают правильное время только один раз в сутки.
Нам надо как-то каждую секунду пересчитывать время и выводить его на экран. Делается это вот как.
Создаем функцию, которая будет это делать, и переносим туда все, что касается пересчета и вывода времени:
static void second_tick(struct tm* tick_time, TimeUnits units_changed) {
static char time[] = "00:00:00";
strftime(time, sizeof(time), "%T", current_time);
text_layer_set_text(text_layer, time);
}
Подписываемся на сервис таймера:
tick_timer_service_subscribe(SECOND_UNIT, &second_tick);
Первый аргумент означает, что функция во втором аргументе будет вызываться каждую секунду. Если в часах не будет секунд, то можно экономить энергию, и будить процессор в 60 раз меньше, вызывая функцию каждую минуту: MINUTE_UNIT
Или каждый час: HOUR_UNIT
Или каждый день: DAY_UNIT
Ну, вы поняли логику: MONTH_UNIT, YEAR_UNIT
После этого желательно вызвать эту функцию принудительно, не дожидаясь очередного тика таймера — ну просто для красоты, чтобы секунду не смотреть на пустой экран. Это хорошо если секунду. А если таймер каждую минуту тикает?
second_tick(current_time, SECOND_UNIT);
В итоге наши часы выглядят вот так:
Исходник:
#include "pebble.h"
Window *window;
TextLayer *text_layer;
static void second_tick(struct tm* tick_time, TimeUnits units_changed) /* функция, вызываемая при тике таймера */
{
static char time[] = "00:00:00"; /* создаем переменную и определяем ее размер */
strftime(time, sizeof(time), "%T", tick_time); /* форматируем строку с временем */
text_layer_set_text(text_layer, time); /* записываем строку со временем в тестовый слой */
}
int main(void)
{
window = window_create();
window_set_background_color(window, GColorBlack);
window_stack_push(window, true);
text_layer = text_layer_create(GRect(0, 0, 144, 168));
text_layer_set_text_color(text_layer, GColorWhite);
text_layer_set_background_color(text_layer, GColorClear);
text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD));
text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
layer_add_child(window_get_root_layer(window), text_layer_get_layer(text_layer));
time_t now = time(NULL); /* создаем переменную и пишем в нее POSIX-время */
struct tm *current_time = localtime(&now); /* получаем из POSIX значения времени и даты */
tick_timer_service_subscribe(SECOND_UNIT, &second_tick); /* подписываемся на секундный таймер */
second_tick(current_time, SECOND_UNIT); /* вызываем обновление времени не дожидаясь тика */
app_event_loop();
text_layer_destroy(text_layer);
window_destroy(window);
tick_timer_service_unsubscribe();
}
Ссылки:
Httpeble: библиотека, реализующая http-запросы внутри программ[EN]Проект по-настоящему умных часов(календарь, погода, напоминания, поиск телефона, HTTP-запросы для управления умным домом, управление музыкой, управление камерой, GPS, биржевые сводки)
PapaBubaDiop пишет о разработке игры под Pebble.
Огромная коллекция различных приложений для pebble, большинство — с исходниками.