⬆️ ⬇️

Well, Pebble, wait a minute

image

I had another smart watch. Pebble

At first, I wanted to give them to fellow Gadgetans without unpacking. After all, my hands still remember the clock of the future from Google and Sony. They didn’t cause anything but rash and sadness, although the design of the Sony SmartWatch was wonderful.



All right, I think one evening I wash Pebble on my left hand. We have the right hand for the mouse. The clock was not annoying. I was not annoyed. Moreover, a wonderful door opened in the clock, and a treasure behind the door. Natural SDK no fools. That is, a person controls the device, and not vice versa. An old-fashioned C language and a black and white screen - isn't it a miracle !? No hated REST, patterns, repositories and unit-tests. Rejuvenated by 30 years, I made three applications and wrote a small review of the red device and the programming process for Pebble.



Under the button 7 pictures, 7 pieces of code, 7 links and 7 harmful tips.



')

1. The clock is supported by iOS and Android



To synchronize your watch with your iPhone, you must download the corresponding Pebble app from the Appstore.

image

The application is simple and clear, does not require an explanation. The disadvantage is that there is no free version for iPad. The disadvantage is easily cost in the presence of 3 dollars. An advanced user with the help of the application can easily update the clock firmware to the newest one. A developer through the application can download the program created by yourself.



2. How to do it?



There is a completely phenomenal Pebble application development environment . Online development is a miracle. You do not need to download tons of software, run configurators , settings, and call specialists from IT Department.



image



Just go to the site, register and click on the Create project button. Everything! You are a Pebble app developer. Enjoy the C language and the very similar paradigm of drawing bitmaps , text, handling the pressings on the three right buttons and one left one on Windows 3.0. There is a vibration and a function to call it. There is no sound, but I love silence.



In the project settings, the selection is small.

There are two types of applications for Pebble. Actually watch (watchface) and applications (watchapp). I chose the second option.

There are two OS versions (as well as SDK versions) for watches. 1.x and 2.x. I chose the development for the second version - the official release 2.0 promise for the New Year. Projects without changes can be rebuilt under 1.x if you are not using the cool features of the second version.

You can add files and resources to a project with a simple click. Files - text code C, * .c and * .h. Resources - pictures in the format of bit PNG and fonts.



What's next?

On the website of Pebble developers you can use a ready-made set of sample projects. It is located in Pebble SDK; you can download it only after registration. There are about 50 examples and they are wonderful.



3. Download the application on the clock



The application is downloaded to the watch in three clicks using the iPhone, synchronized with the clock.

Go through Safari to the cloudpebble.net development environment from your iPhone / iPad.



image





Executables for Pebble have the extension PBW. That they can be downloaded to the appropriate store.



4. Game development



There are two types of applications for Pebble. Actually watch (watchface) and applications (watchapp).

What should we proceed from when writing application games?

From the screen size and the number of buttons. The clock has three buttons on the right side and 1 button on the left side.

The screen size is 144 by 168 points, but the top statusbar is not cleaned and steals 144 by 16 points of space.



image



I, of course, created a few games. Tetris is perfect for three buttons. I turned the screen orientation 90 degrees, otherwise the game turns into anguish. But Tetris and Arkanoid do not cling. On such a screen, you just have to do it. Well, wait . That thing, with electronics.



5. Design features





At first I decided to chew on the essence of programming under Pebble on the example of the game. Well, wait a minute .

And then I thought, why? I ripped all the text from the examples. It took two hours to write the program, so please forgive the bad code. I am justified by the speed and the fact that it works. There is one file in the project, but precisely main.c.



Text of the program
#include "pebble.h" static int egg_status[8]; static int egg_ticks[8]; static int egg_places[]= { 4, 0, 0, 0, 0, 2, 5, 0, 0, 0, 2, 4, 6, 0, 0, 1, 3, 4, 6, 0, 1, 2, 4, 5, 6, 0 }; static int level = 0; static int levelFlag = 0; static int egg_pos[]= { -2, 72, 0, 75, 4, 78, 9, 83, 14, 88, 20, 94, 0, 0, -2, 22, 0, 25, 4, 28, 9, 33, 14, 38, 21, 44, 0, 0, 137, 30, 134, 34, 130, 38, 127, 43, 121, 48, 114, 54, 0, 0, 137, 83, 134, 86, 131, 89, 127, 94, 121, 99, 114, 104, 0, 0 }; static int current_pos = 1; static int best = 25; static AppTimer *timer; static Window *window; static Layer *layer; // We will use a bitmap to composite with a large circle static GBitmap *image_1; static GBitmap *image_2; static GBitmap *image_3; static GBitmap *image_4; static GBitmap *image_5; static GBitmap *image_7; static GBitmap *image_8; static GBitmap *image_6; static GBitmap *image_0; static int ticks = 0; static int score = 0; static int failed = 0; static int egg_failed = 0; char *itoa(int num); static const VibePattern broken_egg_pattern = { .durations = (uint32_t []) {50, 50, 50}, .num_segments = 3 }; static const VibePattern game_over_pattern = { .durations = (uint32_t []) {300, 100, 300}, .num_segments = 3 }; static void timer_callback(void *context) { if (failed<3) { levelFlag = 0; egg_failed = 0; for (int i=0; i<8; i++) if (egg_status[i]>0) { egg_ticks[i]++; if (egg_ticks[i]==6) { int k = egg_status[i]; if (k==current_pos) { score++; } else { egg_failed = k; ++failed; int game_over = failed == 3; //TODO: endgame screen if (game_over) { vibes_enqueue_custom_pattern(game_over_pattern); } else { vibes_enqueue_custom_pattern(broken_egg_pattern); } } egg_ticks[i] = 0; egg_status[i] = 0; } } int lev = 1; if (score>10) lev = 2; if (score>10*3) lev = 3; if (score>10*10) lev = 4; if (lev!=level) { level = lev; levelFlag = 1; vibes_enqueue_custom_pattern(broken_egg_pattern); } for (int i =0; i<level; i++) { int k = egg_places[i+(level-1)*5]; int j = ticks%8; if (k==j) { egg_ticks[i] = 0; egg_status[i] = 1 + rand()%4; } } if (levelFlag) { ticks=0; for (int i =0; i<8; i++) { egg_status[i] = 0; egg_ticks[i] = 0; } } else { ticks++; } } layer_mark_dirty(layer); const uint32_t timeout_ms = 500; timer = app_timer_register(timeout_ms, timer_callback, NULL); } // This is a layer update callback where compositing will take place static void layer_update_callback(Layer *layer, GContext* ctx) { char title[20]; GRect bounds = layer_get_frame(layer); GRect destination = image_1->bounds; destination.origin.x = (bounds.size.w-destination.size.w)/2; destination.origin.y = 30; if (failed<3) { if (levelFlag) { snprintf(title, 20, "\nLevel \n%d", level); // Display the name of the current compositing operation graphics_context_set_text_color(ctx, GColorBlack); graphics_draw_text(ctx, title, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), bounds, GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); } else { snprintf(title, 20, "%d/%d", score, best); // Display the name of the current compositing operation graphics_context_set_text_color(ctx, GColorBlack); graphics_draw_text(ctx, title, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD), bounds, GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); // strcpy(title, "Lev "); // strcat(title, itoa(level)); // Draw the large circle the image will composite with // graphics_context_set_fill_color(ctx, GColorBlack); // graphics_fill_circle(ctx, GPoint(bounds.size.w/2, bounds.size.h+110), 180); // Use the image size to help center the image // Center horizontally using the window frame size // Set the current compositing operation // This will only cause bitmaps to composite // Draw the bitmap; it will use current compositing operation set if (current_pos==1) graphics_draw_bitmap_in_rect(ctx, image_1, destination); if (current_pos==2) graphics_draw_bitmap_in_rect(ctx, image_2, destination); if (current_pos==3) graphics_draw_bitmap_in_rect(ctx, image_3, destination); if (current_pos==4) graphics_draw_bitmap_in_rect(ctx, image_4, destination); GRect ground = image_0->bounds; ground.origin.x = (bounds.size.w-ground.size.w)/2; ground.origin.y = 30; graphics_context_set_compositing_mode(ctx, GCompOpAnd); graphics_draw_bitmap_in_rect(ctx, image_0, ground); for (int i=0; i<8; i++) if (egg_status[i]>0) { int img= egg_ticks[i]%2; int k = (egg_ticks[i]+(egg_status[i]-1)*7) *2; int x = egg_pos[k]; int y = egg_pos[k+1]; GRect egg_destination = image_5->bounds; egg_destination.origin.x = x; egg_destination.origin.y = y; graphics_draw_bitmap_in_rect(ctx, (img) ? image_5 : image_7, egg_destination); } if (egg_failed) { GRect egg_destination = image_6->bounds; egg_destination.origin.x = egg_failed < 3 ? 10 : 101; egg_destination.origin.y = 130; graphics_draw_bitmap_in_rect(ctx, image_6, egg_destination); } } } else { if (score>best) best = score; snprintf(title, 20, "\nLast %d\nBest %d", score, best); // Display the name of the current compositing operation graphics_context_set_text_color(ctx, GColorBlack); graphics_draw_text(ctx, title, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD), bounds, GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); GRect egg_destination = image_6->bounds; egg_destination.origin.y = 130; egg_destination.origin.x = 10; graphics_draw_bitmap_in_rect(ctx, image_6, egg_destination); egg_destination.origin.x = 56; graphics_draw_bitmap_in_rect(ctx, image_6, egg_destination); egg_destination.origin.x = 101; graphics_draw_bitmap_in_rect(ctx, image_6, egg_destination); } } static void select_click_handler(ClickRecognizerRef recognizer, void *context) { if (failed==3) { level = 0; score = 0; for (int i =0; i<8; i++) { egg_status[i] = 0; egg_ticks[i] = 0; } failed = 0; } layer_mark_dirty(layer); } static void up_click_handler(ClickRecognizerRef recognizer, void *context) { current_pos--; if (current_pos<1) current_pos = 4; layer_mark_dirty(layer); } static void down_click_handler(ClickRecognizerRef recognizer, void *context) { current_pos++; if (current_pos>4) current_pos = 1; layer_mark_dirty(layer); } static void config_provider(void *context) { window_single_click_subscribe(BUTTON_ID_UP, up_click_handler); window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler); window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler); } int main(void) { time_t t = 0; uint16_t t_ms = 0; time_ms(&t, &t_ms); srand(t * 1000 + t_ms); // Then use the respective resource loader to obtain the resource for use // In this case, we load the image image_1 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_WOLF_1); image_2 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_WOLF_2); image_3 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_WOLF_3); image_4 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_WOLF_4); image_5 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_EGG_1); image_6 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_EGG_0); image_7 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_EGG_2); image_8 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_EGG_3); image_0 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_GROUND); window = window_create(); window_stack_push(window, true /* Animated */); window_set_click_config_provider(window, config_provider); // Initialize the layer Layer *window_layer = window_get_root_layer(window); GRect bounds = layer_get_frame(window_layer); layer = layer_create(bounds); // Set up the update layer callback layer_set_update_proc(layer, layer_update_callback); // Add the layer to the window for display layer_add_child(window_layer, layer); const uint32_t timeout_ms = 500; timer = app_timer_register(timeout_ms, timer_callback, NULL); for (int i =0; i<8; i++) { egg_status[i] = 0; egg_ticks[i] = 0; } // Enter the main loop app_event_loop(); // Cleanup the image gbitmap_destroy(image_1); gbitmap_destroy(image_2); gbitmap_destroy(image_3); gbitmap_destroy(image_4); gbitmap_destroy(image_5); gbitmap_destroy(image_6); gbitmap_destroy(image_7); gbitmap_destroy(image_0); gbitmap_destroy(image_8); layer_destroy(layer); window_destroy(window); } 






I will comment a little bit.

 #include "pebble.h" int main(void) { // Then use the respective resource loader to obtain the resource for use // In this case, we load the image image_1 = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_WOLF_1); window = window_create(); window_stack_push(window, true /* Animated */); // Initialize the layer Layer *window_layer = window_get_root_layer(window); GRect bounds = layer_get_frame(window_layer); layer = layer_create(bounds); // Set up the update layer callback layer_set_update_proc(layer, layer_update_callback); // Add the layer to the window for display layer_add_child(window_layer, layer); // Enter the main loop app_event_loop(); // Cleanup the image gbitmap_destroy(image_1); layer_destroy(layer); window_destroy(window); } 


In the main () function, the initialization functions of the main window, the loading of bitmaps from resources, and the naming of the functions for handling clicks are standardly called.

After that we twist the infinite loop.

 app_event_loop(); 


As in Windows 3.1.

After an endless loop, remember to free all resources and the main window.

Everything.



In addition, all standard functions are available for igrodelov, including rand ();



6. Features watches version 2.0





And what about Russian fonts, Mithgol will ask?

For lovers of the Russian language there is a dirty hack . I did not check.



7. Application Store



Register on the site and post the application .



image



I laid out one of my own, suddenly here on Habré there are owners of the wonderful Pebble.

Thanks for reading.



Shl. My friend downloaded the application and a little bit did not get up to 200 eggs. And who will pick up 200 eggs, he will see ...

Source: https://habr.com/ru/post/202164/



All Articles