Pull to refresh
0
Madrobots
Приближаем сингулярность за ваши деньги

Программируем под Pebble. Урок первый: Глупые часы

Reading time 7 min
Views 54K
Когда мне привезли часы 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прозрачная краска прозрачный фон, через который будет виден предыдущий слой(если бы он был). По умолчанию он равен White, вот почему у первой программы был белый фон. Само-собой, это надо делать уже после функции window_create.
Для того, чтобы вывести текст, нам потребуется текстовый слой.
Создаем слой:
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

Делаем ребенка Добавляем созданный нами слой к слою главного экрана в качестве дочернего:
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, большинство — с исходниками.


Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+35
Comments 42
Comments Comments 42

Articles

Information

Website
madrobots.ru
Registered
Founded
Employees
51–100 employees