Matthew Kellogg – Pebble – Snowfall

I originally wanted to make a water droplet fall into the screen each second, filling the screen each hour, but I quickly ran into trouble with the memory restrictions on the Pebble. I took a long time considering possibilities of what I could do with little to no memory. I wanted to make something visually pleasing. I thought about what could be easier than water and remembered a simple snow screensaver I had made in my Introduction to Computer Programming class in high school. I was probably reminded of this due to the weather conditions. After making the snow work on the screen, I realized what I’d made did not tell time. For this reason, I added a ground which would accumulate and be relieved of snow on an hourly basis. I then added a sun and moon that would pass through the sky telling the time of day. I based the sun and moon motion based on a sunrise and sunset at 6 AM and 6 PM with the moon taking the other half of the day.

I am happy with the final project. I enjoy the simplicity of how I’ve shown the progress through the day because it is divorced from our numeric concept of time.

At 2:10 PM:
pebble-2_10P
At 3:40 AM:
pebble-3_40
At 6:50 AM:
pebble-6_50

 

These are sketches for my final and original idea.

pebble-sketch

Here is the final code:

//Snow Clock
// by: Matthew Kellogg
// date: January 27, 2015
// Copyright 2015 Matthew Kellogg

#include "pebble.h"

static Window *window;

static GRect window_frame;

static Layer *root_layer;

#define WIDTH 144
#define HEIGHT 168

//An array of the x positions of the snowflakes
// there is one snowflake at each y coordinate
static uint8_t flakes_x[HEIGHT];

//This is called whenever the layer needs redrawn.
//This draws all of the scene
static void layer_update_callback(Layer *me, GContext *ctx) {
  //draw
  
  //clear
  graphics_context_set_fill_color(ctx, GColorBlack);
  graphics_fill_rect(ctx, window_frame, 0, GCornerNone);
  
  //get time
  time_t t = time(NULL);
  struct tm* time = localtime(&t);
  int buffer = 76;
  int done = ((HEIGHT-buffer)*((60*time->tm_min) + time->tm_sec)) / 3600;
  
  //draw snow base
  GRect snow_rect = GRect(0, HEIGHT-done, WIDTH, done);
  graphics_context_set_fill_color(ctx, GColorWhite);
  graphics_fill_rect(ctx, snow_rect,0 , GCornerNone);
  
  //draw sun/ moon
  // sun rise at 6AM set at 6PM, moon rise and set at 6 PM and 6 AM
  int sunx = (WIDTH*(((time->tm_hour+6)%12))*60 +time->tm_min)/(12*60);
  graphics_fill_circle(ctx,GPoint(sunx,56), 20);
  if (time->tm_hour >= 18 || time->tm_hour<6){
    graphics_context_set_fill_color(ctx, GColorBlack);
    graphics_fill_circle(ctx, GPoint(sunx+10,56), 14);
    graphics_context_set_fill_color(ctx, GColorWhite);
  }
  
  //draw flakes
  graphics_context_set_stroke_color(ctx, GColorWhite);
  int i,y;
  for (i=0;i<HEIGHT; i++){     y = i%HEIGHT;     graphics_fill_circle(ctx,GPoint(flakes_x[y],y),2);   } } //This is called once per second //It updates snowflake positions and marks the layer for redraw static void timer_callback(struct tm *tick_time, TimeUnits units_changed){   //update   int i;   for (i = HEIGHT -1; i >= 0; i--){
    flakes_x[(i+1)%HEIGHT] = (int)flakes_x[i]-1+(rand()%3);
  }
  
  layer_mark_dirty(root_layer);
}
                        
//This initializes the snowflake array with random coordinates
static void flakes_init(){
  int i;
  for (i = 0; i< HEIGHT; i++){
    flakes_x[i] = rand()%HEIGHT;
  }
}

//This is called when the window is available
// It gets the layer and sets the update function
static void window_load(Window *window) {
  root_layer = window_get_root_layer(window);
  window_frame = layer_get_frame(root_layer);
  layer_set_update_proc(root_layer, layer_update_callback);
}

//This function is empty because no dynamic memory is used
static void window_unload(Window *window) {
}

//This method is called when the app is being initialized
static void init(void) {
  window = window_create();
  window_set_window_handlers(window, (WindowHandlers) {
    .load = window_load,
    .unload = window_unload
  });
  window_stack_push(window, true /* Animated */);
  window_set_background_color(window, GColorBlack);

  //initialize flakes
  flakes_init();
  
  tick_timer_service_subscribe(SECOND_UNIT, timer_callback);
}

static void deinit(void) {
  window_destroy(window);
}

int main(void) {
  init();
  app_event_loop();
  deinit();
}