Upload
matthew-morey
View
115
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Pebble is the first watch built for the 21st century. It is very minimal yet extremely customizable with internet-connected apps. During this session we will look at the technical capabilities of the Pebble and what you can achieve when you connect your iPhone to it. By the end of the session you will know how to take your iOS knowledge and build great Pebble apps.
Citation preview
Pebble Watchmatthewmorey.com | @xzolian
matthewmorey.com | @xzolian
☐ What is the Pebble☐ Why you should develop for the Pebble☐ How to develop for the Pebble
Agenda
Demo
vs
ARM Cortex-M3 MCU 120 MHz
Apple A6ARMv7 1.3 GHz
Dual Core
128 KB RAMUp to 8 apps
1 GB RAMUp to 64 GB storage
144 x 168 pixels1-bit color“e-paper”
1,136 × 640 pixels24-bit Color
“Retina”
Pebble iPhone 5
CPU
Memory
Screen
Bluetooth 2.1 and 4.0 LE
Bluetooth 4.0802.11a/b/g/n Wi-Fi
Cellular LTE
3-axis accelerometerAmbient light
Magnetometer
Three-axis gyroAccelerometer
Proximity sensorAmbient light sensor
4 buttonsVibration
Screen backlight
Touch screenButtons
VibrationCamera
Pebble iPhone 5
Comm.
Sensors
IO
2 to 14 days ~1 day
FreeRTOSPebble SDK
PebbleKit
iOSiOS SDK
5 ATM water resistance
22 mm watch band
Not water resistantCan’t wear on wrist
Pebble iPhone 5
Battery
Software
Other
Issues
☑ What is the Pebble☐ Why you should develop for the Pebble☐ How to develop for the Pebble
Agenda
LunaTik
“I agree to wear you around and share a lot of personal data
with you.”
- Customers
$10,266,846 - Pebble Watch (design)
$8,596,475 - OUYA (Android console)
$5,702,153 - Veronica Mars (movie)
$4,188,927 - Torment: Tides of Numenera (game)
$3,986,929 - Project Eternity (game)
$6 Billionper year
Units Sold
Pebble iPhone
>100,000
>250 Million
iOS 7
☑ What is the Pebble☑ Why you should develop for the Pebble☐ How to develop for the Pebble
Agenda
Pebble SDK
1. Pebble SDK dependencies2. Pebble SDK3. ARM dependencies4. Pebble ARM toolchain
Install
Hello World
$ create_pebble_project.py \ > PebbleSDK-1.12/Pebble/sdk \ > hello_world
1 #include "pebble_os.h" 2 #include "pebble_app.h" 3 #include "pebble_fonts.h" 4 5 #define MY_UUID { 6 0x20, 0xD9, 0xFB, 0x8F, 0xBE, 0xCF, 0x49, 7 0x94, 0xA9, 0xE8, 0x70, 0xC0, 0xEA, 0xC6, 8 0x50, 0x16 9 }10 11 PBL_APP_INFO(12 MY_UUID,13 "Hello World",14 "Acme Corp",15 1, 0, /* App version */16 DEFAULT_MENU_ICON,17 APP_INFO_STANDARD_APP18 );
1 #include "pebble_os.h" 2 #include "pebble_app.h" 3 #include "pebble_fonts.h" 4 5 #define MY_UUID { 6 0x20, 0xD9, 0xFB, 0x8F, 0xBE, 0xCF, 0x49, 7 0x94, 0xA9, 0xE8, 0x70, 0xC0, 0xEA, 0xC6, 8 0x50, 0x16 9 }10 11 PBL_APP_INFO(12 MY_UUID,13 "Hello World",14 "Acme Corp",15 1, 0, /* App version */16 DEFAULT_MENU_ICON,17 APP_INFO_STANDARD_APP18 );
1 #include "pebble_os.h" 2 #include "pebble_app.h" 3 #include "pebble_fonts.h" 4 5 #define MY_UUID { 6 0x20, 0xD9, 0xFB, 0x8F, 0xBE, 0xCF, 0x49, 7 0x94, 0xA9, 0xE8, 0x70, 0xC0, 0xEA, 0xC6, 8 0x50, 0x16 9 }10 11 PBL_APP_INFO(12 MY_UUID,13 "Hello World",14 "Acme Corp",15 1, 0, /* App version */16 DEFAULT_MENU_ICON,17 APP_INFO_STANDARD_APP18 );
20 Window window;21 22 void handle_init(AppContextRef ctx) {23 window_init(&window, "Window Name");24 window_stack_push(&window,true/*Animated*/);25 }26 27 28 void pbl_main(void *params) {29 PebbleAppHandlers handlers = {30 .init_handler = &handle_init31 };32 app_event_loop(params, &handlers);33 }
20 Window window;21 22 void handle_init(AppContextRef ctx) {23 window_init(&window, "Window Name");24 window_stack_push(&window,true/*Animated*/);25 }26 27 28 void pbl_main(void *params) {29 PebbleAppHandlers handlers = {30 .init_handler = &handle_init31 };32 app_event_loop(params, &handlers);33 }
20 Window window;21 22 void handle_init(AppContextRef ctx) {23 window_init(&window, "Window Name");24 window_stack_push(&window,true/*Animated*/);25 }26 27 28 void pbl_main(void *params) {29 PebbleAppHandlers handlers = {30 .init_handler = &handle_init31 };32 app_event_loop(params, &handlers);33 }
20 Window window;21 22 void handle_init(AppContextRef ctx) {23 window_init(&window, "Window Name");24 window_stack_push(&window,true/*Animated*/);25 }26 27 28 void pbl_main(void *params) {29 PebbleAppHandlers handlers = {30 .init_handler = &handle_init31 };32 app_event_loop(params, &handlers);33 }
15 TextLayer hello_layer;16 17 void handle_init(AppContextRef ctx) {18 19 window_init(&window, "Window Name");20 window_stack_push(&window, true /* Animated */);21 22 text_layer_init(&hello_layer, GRect(0, 65, 144, 30));23 text_layer_set_text_alignment(&hello_layer,GTextAlignmentCenter);24 text_layer_set_text(&hello_layer, "Hello World!");25 text_layer_set_font(26 &hello_layer,27 fonts_get_system_font(FONT_KEY_ROBOTO_CONDENSED_21)28 );2930 layer_add_child(&window.layer, &hello_layer.layer);31 32 }
15 TextLayer hello_layer;16 17 void handle_init(AppContextRef ctx) {18 19 window_init(&window, "Window Name");20 window_stack_push(&window, true /* Animated */);21 22 text_layer_init(&hello_layer, GRect(0, 65, 144, 30));23 text_layer_set_text_alignment(&hello_layer,GTextAlignmentCenter);24 text_layer_set_text(&hello_layer, "Hello World!");25 text_layer_set_font(26 &hello_layer,27 fonts_get_system_font(FONT_KEY_ROBOTO_CONDENSED_21)28 );2930 layer_add_child(&window.layer, &hello_layer.layer);31 32 }
$ ./waf configure$ ./waf build$ python -m SimpleHTTPServer 8000
$ ./waf configure$ ./waf build$ python -m SimpleHTTPServer 8000
Demo
PBW Filehello_world.pbw (zip archive)
app_resources.pbpack (binary)
manifest.json (txt/json)
pebble-app.bin (binary)
App Watch Face
Sports Apps
Event Handlers
On-screen Layers
Resources
Window Stack
View Lifecycle
load
appear
disappear
unload
viewDidLoad
viewWillAppear
viewDidAppear
viewWillDisappear
viewDidDisappear
Pebble iOS
Layers
Layers10
Layers20
10
Frame
Layers
20
20
Frame
Layers
-100
Bounds
Layers
60
20
Bounds
animation_init() ...animation_schedule()
.update called
.update called ....update called
Animation scheduled
.stopped called
App Pebble OS
Animation
Animation stopped
.started called
Input Handlers
Resourcesraw
png
png-trans
font
Debuggingapp_log
vibes_short_pulsevibes_double_pulsevibes_long_pulse
light_enable_interaction
Watch <=> Phone
app_message_out_send() Dictionary
ACK .out_sent calledAccepted
app_message_out_send() Dictionary
NACK .out_failed calledRejected
Pebble Phone
AppMessage
.in_received called Dictionary
ACK
Sending
Pebble Phone
AppMessage
.in_received called Dictionary
NACK
Sending
app_sync_set(...)
tuple_changed
Accepted
Sending
Pebble Phone
AppSync
190 - (void)setTargetWatch:(PBWatch*)watch {191 192 self.watch = watch;193 194 // Test if the Pebble's firmware supports AppMessages195 [watch appMessagesGetIsSupported: ^(PBWatch *watch, BOOL isAppMessagesSupported) {196197 if (isAppMessagesSupported) {198 199 // Configure communications channel to target the weather app:200 uint8_t bytes[] = {201 0x42, 0xc8, 0x6e, 0xa4, 0x1c, 0x3e, 0x4a, 0x07, 202 0xb8, 0x89, 0x2c, 0xcc, 0xca, 0x91, 0x41, 0x98};203 NSData *uuid = [NSData dataWithBytes:bytes length:sizeof(bytes)];204 [watch appMessagesSetUUID:uuid];205 206 [self updateWatch];207 208 } 209210 }];211 } iOS
190 - (void)setTargetWatch:(PBWatch*)watch {191 192 self.watch = watch;193 194 // Test if the Pebble's firmware supports AppMessages195 [watch appMessagesGetIsSupported: ^(PBWatch *watch, BOOL isAppMessagesSupported) {196197 if (isAppMessagesSupported) {198 199 // Configure communications channel to target the weather app:200 uint8_t bytes[] = {201 0x42, 0xc8, 0x6e, 0xa4, 0x1c, 0x3e, 0x4a, 0x07, 202 0xb8, 0x89, 0x2c, 0xcc, 0xca, 0x91, 0x41, 0x98};203 NSData *uuid = [NSData dataWithBytes:bytes length:sizeof(bytes)];204 [watch appMessagesSetUUID:uuid];205 206 [self updateWatch];207 208 } 209210 }];211 } iOS
190 - (void)setTargetWatch:(PBWatch*)watch {191 192 self.watch = watch;193 194 // Test if the Pebble's firmware supports AppMessages195 [watch appMessagesGetIsSupported: ^(PBWatch *watch, BOOL isAppMessagesSupported) {196197 if (isAppMessagesSupported) {198 199 // Configure communications channel to target the weather app:200 uint8_t bytes[] = {201 0x42, 0xc8, 0x6e, 0xa4, 0x1c, 0x3e, 0x4a, 0x07, 202 0xb8, 0x89, 0x2c, 0xcc, 0xca, 0x91, 0x41, 0x98};203 NSData *uuid = [NSData dataWithBytes:bytes length:sizeof(bytes)];204 [watch appMessagesSetUUID:uuid];205 206 [self updateWatch];207 208 } 209210 }];211 } iOS
259 - (void)updateWatch {260 261 NSDictionary *update = @{262 kKeyWatchIcon:[NSNumber numberWithUint8:1],263 kKeyWatchTemperature:[NSString stringWithFormat:@"%.1f \u00B0F", @”90”]264 };265 266 [self.watch appMessagesPushUpdate:update onSent: ^(PBWatch *watch, NSDictionary *update, NSError *error) {267 268 if (error) {269 // Message was not sent270 } else {271 // Message was sent272 }273 274 }];275 276 }
iOS
259 - (void)updateWatch {260 261 NSDictionary *update = @{262 kKeyWatchIcon:[NSNumber numberWithUint8:1],263 kKeyWatchTemperature:[NSString stringWithFormat:@"%.1f \u00B0F", @”90”]264 };265 266 [self.watch appMessagesPushUpdate:update onSent: ^(PBWatch *watch, NSDictionary *update, NSError *error) {267 268 if (error) {269 // Message was not sent270 } else {271 // Message was sent272 }273 274 }];275 276 }
iOS
259 - (void)updateWatch {260 261 NSDictionary *update = @{262 kKeyWatchIcon:[NSNumber numberWithUint8:1],263 kKeyWatchTemperature:[NSString stringWithFormat:@"%.1f \u00B0F", @”90”]264 };265 266 [self.watch appMessagesPushUpdate:update onSent: ^(PBWatch *watch, NSDictionary *update, NSError *error) {267 268 if (error) {269 // Message was not sent270 } else {271 // Message was sent272 }273 274 }];275 276 }
iOS
296 - (BOOL)handleWatchUpdate:(PBWatch *)watch message:(NSDictionary *)message {299 300 if ([message objectForKey:kKeyWatchRequestUpdate]) {301 NSLog(@"Forecast update requested");303 return YES;304 }305 306 return NO;307 308 }
402 self.updateHandler = [self.watch appMessagesAddReceiveUpdateHandler: ^BOOL(PBWatch *watch, NSDictionary *update) {403 return [self handleWatchUpdate:watch message:update];404 }];
iOS
296 - (BOOL)handleWatchUpdate:(PBWatch *)watch message:(NSDictionary *)message {299 300 if ([message objectForKey:kKeyWatchRequestUpdate]) {301 NSLog(@"Forecast update requested");303 return YES;304 }305 306 return NO;307 308 }
402 self.updateHandler = [self.watch appMessagesAddReceiveUpdateHandler: ^BOOL(PBWatch *watch, NSDictionary *update) {403 return [self handleWatchUpdate:watch message:update];404 }];
iOS
296 - (BOOL)handleWatchUpdate:(PBWatch *)watch message:(NSDictionary *)message {299 300 if ([message objectForKey:kKeyWatchRequestUpdate]) {301 NSLog(@"Forecast update requested");303 return YES;304 }305 306 return NO;307 308 }
402 self.updateHandler = [self.watch appMessagesAddReceiveUpdateHandler: ^BOOL(PBWatch *watch, NSDictionary *update) {403 return [self handleWatchUpdate:watch message:update];404 }];
iOS
171 void pbl_main(void *params) {172 173 PebbleAppHandlers handlers = {174 175 .init_handler = &app_init,176 .deinit_handler = &app_deinit,177 .messaging_info = {178 .buffer_sizes = {179 .inbound = 64,180 .outbound = 16,181 }182 },183 // Handle time updates184 .tick_info = {185 .tick_handler = &handle_minute_tick,186 .tick_units = MINUTE_UNIT187 }188 };189 190 app_event_loop(params, &handlers);191 192 }
Pebble
171 void pbl_main(void *params) {172 173 PebbleAppHandlers handlers = {174 175 .init_handler = &app_init,176 .deinit_handler = &app_deinit,177 .messaging_info = {178 .buffer_sizes = {179 .inbound = 64,180 .outbound = 16,181 }182 },183 // Handle time updates184 .tick_info = {185 .tick_handler = &handle_minute_tick,186 .tick_units = MINUTE_UNIT187 }188 };189 190 app_event_loop(params, &handlers);191 192 }
Pebble
171 void pbl_main(void *params) {172 173 PebbleAppHandlers handlers = {174 175 .init_handler = &app_init,176 .deinit_handler = &app_deinit,177 .messaging_info = {178 .buffer_sizes = {179 .inbound = 64,180 .outbound = 16,181 }182 },183 // Handle time updates184 .tick_info = {185 .tick_handler = &handle_minute_tick,186 .tick_units = MINUTE_UNIT187 }188 };189 190 app_event_loop(params, &handlers);191 192 }
Pebble
171 void pbl_main(void *params) {172 173 PebbleAppHandlers handlers = {174 175 .init_handler = &app_init,176 .deinit_handler = &app_deinit,177 .messaging_info = {178 .buffer_sizes = {179 .inbound = 64,180 .outbound = 16,181 }182 },183 // Handle time updates184 .tick_info = {185 .tick_handler = &handle_minute_tick,186 .tick_units = MINUTE_UNIT187 }188 };189 190 app_event_loop(params, &handlers);191 192 }
Pebble
171 void pbl_main(void *params) {172 173 PebbleAppHandlers handlers = {174 175 .init_handler = &app_init,176 .deinit_handler = &app_deinit,177 .messaging_info = {178 .buffer_sizes = {179 .inbound = 64,180 .outbound = 16,181 }182 },183 // Handle time updates184 .tick_info = {185 .tick_handler = &handle_minute_tick,186 .tick_units = MINUTE_UNIT187 }188 };189 190 app_event_loop(params, &handlers);191 192 }
Pebble
116 static void app_init(AppContextRef c) {117118 resource_init_current_app(&WEATHER_APP_RESOURCES);119 120 Window* window = &s_data.window;121 window_init(window, "PebbleWeather");122 window_set_background_color(window, GColorBlack);123 window_set_fullscreen(window, true);124
...149 // Watch <--> Phone communication150 Tuplet initial_values[] = {151 TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1),152 TupletCString(WEATHER_TEMPERATURE_KEY, "-\u00B0C"),153 };154 app_sync_init(&s_data.sync, s_data.sync_buffer, sizeof(s_data.sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL);155 156 window_stack_push(window, true);157 168 } Pebble
116 static void app_init(AppContextRef c) {117118 resource_init_current_app(&WEATHER_APP_RESOURCES);119 120 Window* window = &s_data.window;121 window_init(window, "PebbleWeather");122 window_set_background_color(window, GColorBlack);123 window_set_fullscreen(window, true);124
...149 // Watch <--> Phone communication150 Tuplet initial_values[] = {151 TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1),152 TupletCString(WEATHER_TEMPERATURE_KEY, "-\u00B0C"),153 };154 app_sync_init(&s_data.sync, s_data.sync_buffer, sizeof(s_data.sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL);155 156 window_stack_push(window, true);157 168 } Pebble
116 static void app_init(AppContextRef c) {117118 resource_init_current_app(&WEATHER_APP_RESOURCES);119 120 Window* window = &s_data.window;121 window_init(window, "PebbleWeather");122 window_set_background_color(window, GColorBlack);123 window_set_fullscreen(window, true);124
...149 // Watch <--> Phone communication150 Tuplet initial_values[] = {151 TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1),152 TupletCString(WEATHER_TEMPERATURE_KEY, "-\u00B0C"),153 };154 app_sync_init(&s_data.sync, s_data.sync_buffer, sizeof(s_data.sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL);155 156 window_stack_push(window, true);157 168 } Pebble
116 static void app_init(AppContextRef c) {117118 resource_init_current_app(&WEATHER_APP_RESOURCES);119 120 Window* window = &s_data.window;121 window_init(window, "PebbleWeather");122 window_set_background_color(window, GColorBlack);123 window_set_fullscreen(window, true);124
...149 // Watch <--> Phone communication150 Tuplet initial_values[] = {151 TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1),152 TupletCString(WEATHER_TEMPERATURE_KEY, "-\u00B0C"),153 };154 app_sync_init(&s_data.sync, s_data.sync_buffer, sizeof(s_data.sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL);155 156 window_stack_push(window, true);157 168 } Pebble
104 // Send message to phone asking for a weather update105 void request_weather_update() {106 107 vibes_double_pulse();108 109 Tuplet values[] = {110 TupletInteger(WEATHER_FORECAST_REQUEST_KEY, 1),111 };112 app_sync_set(&s_data.sync, values, ARRAY_LENGTH(values));113 114 }
Pebble
104 // Send message to phone asking for a weather update105 void request_weather_update() {106 107 vibes_double_pulse();108 109 Tuplet values[] = {110 TupletInteger(WEATHER_FORECAST_REQUEST_KEY, 1),111 };112 app_sync_set(&s_data.sync, values, ARRAY_LENGTH(values));113 114 }
Pebble
65 // Tuple changed 66 static void sync_tuple_changed_callback( const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { 67 68 switch (key) { 69 case WEATHER_ICON_KEY: 70 load_bitmap(WEATHER_ICONS[new_tuple->value->uint8]); 71 bitmap_layer_set_bitmap( &s_data.icon_layer, &s_data.icon_bitmap.bmp ); 72 break; 73 74 case WEATHER_TEMPERATURE_KEY: 75 text_layer_set_text( &s_data.temperature_layer, new_tuple->value->cstring ); 76 break; 77 78 default: 79 return; 80 } 81 } Pebble
65 // Tuple changed 66 static void sync_tuple_changed_callback( const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { 67 68 switch (key) { 69 case WEATHER_ICON_KEY: 70 load_bitmap(WEATHER_ICONS[new_tuple->value->uint8]); 71 bitmap_layer_set_bitmap( &s_data.icon_layer, &s_data.icon_bitmap.bmp ); 72 break; 73 74 case WEATHER_TEMPERATURE_KEY: 75 text_layer_set_text( &s_data.temperature_layer, new_tuple->value->cstring ); 76 break; 77 78 default: 79 return; 80 } 81 } Pebble
65 // Tuple changed 66 static void sync_tuple_changed_callback( const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { 67 68 switch (key) { 69 case WEATHER_ICON_KEY: 70 load_bitmap(WEATHER_ICONS[new_tuple->value->uint8]); 71 bitmap_layer_set_bitmap( &s_data.icon_layer, &s_data.icon_bitmap.bmp ); 72 break; 73 74 case WEATHER_TEMPERATURE_KEY: 75 text_layer_set_text( &s_data.temperature_layer, new_tuple->value->cstring ); 76 break; 77 78 default: 79 return; 80 } 81 } Pebble
☑ What is the Pebble☑ Why you should develop for the Pebble☑ How to develop for the Pebble
Agenda
Questions?
matthewmorey.com | @xzolian
CreditsPicturesPebble, Pebble Forums, Pebble SubReddit, iFixIt, Wired, Verge, Lifehacker, BBC, MyPebbleFaces, Kickstarter, indiegogo, Wikipedia