Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Pebble Watch iOS SDK Overview

1,630 views

Published on

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.

Published in: Technology, Art & Photos
  • Be the first to comment

  • Be the first to like this

Pebble Watch iOS SDK Overview

  1. 1. Pebble Watch matthewmorey.com | @xzolian
  2. 2. matthewmorey.com | @xzolian
  3. 3. http://buoyexplorer.com
  4. 4. ☐ What is the Pebble ☐ Why you should develop for the Pebble ☐ How to develop for the Pebble Agenda
  5. 5. Demo
  6. 6. vs
  7. 7. ARM Cortex-M3 MCU 120 MHz Apple A6 ARMv7 1.3 GHz Dual Core 128 KB RAM Up to 8 apps 1 GB RAM Up to 64 GB storage 144 x 168 pixels 1-bit color “e-paper” 1,136 × 640 pixels 24-bit Color “Retina” Pebble iPhone 5 CPU Memory Screen
  8. 8. Bluetooth 2.1 and 4.0 LE Bluetooth 4.0 802.11a/b/g/n Wi-Fi Cellular LTE 3-axis accelerometer Ambient light Magnetometer Three-axis gyro Accelerometer Proximity sensor Ambient light sensor 4 buttons Vibration Screen backlight Touch screen Buttons Vibration Camera Pebble iPhone 5 Comm. Sensors IO
  9. 9. 2 to 14 days ~1 day FreeRTOS Pebble SDK PebbleKit iOS iOS SDK 5 ATM water resistance 22 mm watch band Not water resistant Can’t wear on wrist Pebble iPhone 5 Battery Software Other
  10. 10. Issues
  11. 11. ☑ What is the Pebble ☐ Why you should develop for the Pebble ☐ How to develop for the Pebble Agenda
  12. 12. LunaTik
  13. 13. “I agree to wear you around and share a lot of personal data with you.” - Customers
  14. 14. $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)
  15. 15. $6 Billion per year
  16. 16. Units Sold Pebble iPhone >100,000 >250 Million
  17. 17. iOS 7
  18. 18. ☑ What is the Pebble ☑ Why you should develop for the Pebble ☐ How to develop for the Pebble Agenda
  19. 19. Pebble SDK
  20. 20. 1. Pebble SDK dependencies 2. Pebble SDK 3. ARM dependencies 4. Pebble ARM toolchain Install
  21. 21. Hello World $ create_pebble_project.py > PebbleSDK-1.12/Pebble/sdk > hello_world
  22. 22. 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_APP 18 );
  23. 23. 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_APP 18 );
  24. 24. 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_APP 18 );
  25. 25. 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_init 31 }; 32 app_event_loop(params, &handlers); 33 }
  26. 26. 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_init 31 }; 32 app_event_loop(params, &handlers); 33 }
  27. 27. 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_init 31 }; 32 app_event_loop(params, &handlers); 33 }
  28. 28. 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_init 31 }; 32 app_event_loop(params, &handlers); 33 }
  29. 29. 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 ); 29 30 layer_add_child(&window.layer, &hello_layer.layer); 31 32 }
  30. 30. 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 ); 29 30 layer_add_child(&window.layer, &hello_layer.layer); 31 32 }
  31. 31. $ ./waf configure $ ./waf build $ python -m SimpleHTTPServer 8000
  32. 32. $ ./waf configure $ ./waf build $ python -m SimpleHTTPServer 8000
  33. 33. Demo
  34. 34. PBW File hello_world.pbw (zip archive) app_resources.pbpack (binary) manifest.json (txt/json) pebble-app.bin (binary)
  35. 35. App Watch Face
  36. 36. Sports Apps
  37. 37. Event Handlers On-screen Layers Resources
  38. 38. Window Stack
  39. 39. View Lifecycle load appear disappear unload viewDidLoad viewWillAppear viewDidAppear viewWillDisappear viewDidDisappear Pebble iOS
  40. 40. Layers
  41. 41. Layers 10
  42. 42. Layers 20 10 Frame
  43. 43. Layers 20 20 Frame
  44. 44. Layers -10 0 Bounds
  45. 45. Layers 60 20 Bounds
  46. 46. animation_init() ... animation_schedule() .update called .update called ... .update called Animation scheduled .stopped called App Pebble OS Animation Animation stopped .started called
  47. 47. Input Handlers
  48. 48. Resources raw png png-trans font
  49. 49. Debugging app_log vibes_short_pulse vibes_double_pulse vibes_long_pulse light_enable_interaction
  50. 50. Watch <=> Phone
  51. 51. app_message_out_send() Dictionary ACK.out_sent called Accepted app_message_out_send() Dictionary NACK.out_failed called Rejected Pebble Phone AppMessage
  52. 52. .in_received called Dictionary ACK Sending Pebble Phone AppMessage .in_received called Dictionary NACK Sending
  53. 53. app_sync_set(...) tuple_changed Accepted Sending Pebble Phone AppSync
  54. 54. 190 - (void)setTargetWatch:(PBWatch*)watch { 191 192 self.watch = watch; 193 194 // Test if the Pebble's firmware supports AppMessages 195 [watch appMessagesGetIsSupported: ^(PBWatch *watch, BOOL isAppMessagesSupported) { 196 197 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 } 209 210 }]; 211 } iOS
  55. 55. 190 - (void)setTargetWatch:(PBWatch*)watch { 191 192 self.watch = watch; 193 194 // Test if the Pebble's firmware supports AppMessages 195 [watch appMessagesGetIsSupported: ^(PBWatch *watch, BOOL isAppMessagesSupported) { 196 197 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 } 209 210 }]; 211 } iOS
  56. 56. 190 - (void)setTargetWatch:(PBWatch*)watch { 191 192 self.watch = watch; 193 194 // Test if the Pebble's firmware supports AppMessages 195 [watch appMessagesGetIsSupported: ^(PBWatch *watch, BOOL isAppMessagesSupported) { 196 197 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 } 209 210 }]; 211 } iOS
  57. 57. 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 sent 270 } else { 271 // Message was sent 272 } 273 274 }]; 275 276 } iOS
  58. 58. 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 sent 270 } else { 271 // Message was sent 272 } 273 274 }]; 275 276 } iOS
  59. 59. 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 sent 270 } else { 271 // Message was sent 272 } 273 274 }]; 275 276 } iOS
  60. 60. 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
  61. 61. 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
  62. 62. 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
  63. 63. 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 updates 184 .tick_info = { 185 .tick_handler = &handle_minute_tick, 186 .tick_units = MINUTE_UNIT 187 } 188 }; 189 190 app_event_loop(params, &handlers); 191 192 } Pebble
  64. 64. 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 updates 184 .tick_info = { 185 .tick_handler = &handle_minute_tick, 186 .tick_units = MINUTE_UNIT 187 } 188 }; 189 190 app_event_loop(params, &handlers); 191 192 } Pebble
  65. 65. 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 updates 184 .tick_info = { 185 .tick_handler = &handle_minute_tick, 186 .tick_units = MINUTE_UNIT 187 } 188 }; 189 190 app_event_loop(params, &handlers); 191 192 } Pebble
  66. 66. 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 updates 184 .tick_info = { 185 .tick_handler = &handle_minute_tick, 186 .tick_units = MINUTE_UNIT 187 } 188 }; 189 190 app_event_loop(params, &handlers); 191 192 } Pebble
  67. 67. 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 updates 184 .tick_info = { 185 .tick_handler = &handle_minute_tick, 186 .tick_units = MINUTE_UNIT 187 } 188 }; 189 190 app_event_loop(params, &handlers); 191 192 } Pebble
  68. 68. 116 static void app_init(AppContextRef c) { 117 118 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 communication 150 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
  69. 69. 116 static void app_init(AppContextRef c) { 117 118 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 communication 150 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
  70. 70. 116 static void app_init(AppContextRef c) { 117 118 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 communication 150 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
  71. 71. 116 static void app_init(AppContextRef c) { 117 118 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 communication 150 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
  72. 72. 104 // Send message to phone asking for a weather update 105 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
  73. 73. 104 // Send message to phone asking for a weather update 105 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
  74. 74. 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
  75. 75. 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
  76. 76. 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
  77. 77. ☑ What is the Pebble ☑ Why you should develop for the Pebble ☑ How to develop for the Pebble Agenda
  78. 78. Questions? matthewmorey.com | @xzolian
  79. 79. Credits Pictures Pebble, Pebble Forums, Pebble SubReddit, iFixIt, Wired, Verge, Lifehacker, BBC, MyPebbleFaces, Kickstarter, indiegogo, Wikipedia

×