summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--watchapp/src/actimon.c138
-rw-r--r--watchapp/src/actimon.h19
-rw-r--r--watchapp/src/mainmenu.c64
-rw-r--r--watchapp/src/mainmenu.h10
-rw-r--r--watchapp/src/watchapp.c126
5 files changed, 334 insertions, 23 deletions
diff --git a/watchapp/src/actimon.c b/watchapp/src/actimon.c
new file mode 100644
index 0000000..1002672
--- /dev/null
+++ b/watchapp/src/actimon.c
@@ -0,0 +1,138 @@
+#include <pebble.h>
+#include "actimon.h"
+
+/* Since the user just launched us, we're likely to be stepcounting */
+monitor_mode_e monitor_mode = mmSteps;
+static monitor_mode_e last_monitor_mode = mmSleep;
+uint32_t step_total_today = 0;
+
+static int8_t strunc(int16_t samp)
+{
+ return (int8_t)(samp >> 5);
+}
+
+#define iter1(N) \
+ try = root + (1 << (N)); \
+ if (n >= try << (N)) \
+ { n -= try << (N); \
+ root |= 2 << (N); \
+ }
+
+static uint32_t isqrt(uint32_t n)
+{
+ uint32_t root = 0, try;
+ iter1 (15); iter1 (14); iter1 (13); iter1 (12);
+ iter1 (11); iter1 (10); iter1 ( 9); iter1 ( 8);
+ iter1 ( 7); iter1 ( 6); iter1 ( 5); iter1 ( 4);
+ iter1 ( 3); iter1 ( 2); iter1 ( 1); iter1 ( 0);
+ return root >> 1;
+}
+
+#undef iter1
+
+static uint32_t mag(int16_t x, int16_t y, int16_t z)
+{
+ return isqrt((x*x) + (y*y) + (z*z));
+}
+
+static uint32_t runiir(uint32_t m)
+{
+ static uint32_t z1 = 0;
+ return (z1 = (((z1 << 2) - z1) + m) >> 2);
+}
+
+static uint64_t now;
+
+static uint8_t step_last_mag;
+static int16_t step_last_delta;
+static uint64_t step_last_event;
+static bool step_last_event_was_step;
+static uint16_t step_current_gap;
+
+static void init_steps()
+{
+ step_last_mag = 0;
+ step_last_delta = 0;
+ step_last_event = 0;
+ step_last_event_was_step = false;
+}
+
+static void log_step_point(uint8_t since, uint8_t mag)
+{
+ int32_t delta = mag - step_last_mag;
+ bool maybeevent = (step_last_delta > 0) && (delta <= 0);
+ step_last_mag = mag;
+ step_last_delta = delta;
+ if (maybeevent) {
+ uint32_t steptime = (uint32_t)(now - step_last_event);
+ if (steptime >= (uint32_t)(step_current_gap - 150) &&
+ steptime <= (uint32_t)(step_current_gap + 150)) {
+ /* Event detected */
+ step_total_today++;
+ if (!step_last_event_was_step)
+ step_total_today++;
+ step_last_event_was_step = true;
+ } else {
+ step_last_event_was_step = false;
+ }
+ step_last_event = now;
+ if (steptime < 2000) {
+ step_current_gap = ((step_current_gap << 3) - step_current_gap + steptime) >> 3;
+ if (step_current_gap < 425) step_current_gap = 425;
+ if (step_current_gap > 675) step_current_gap = 675;
+ }
+ }
+}
+
+static void log_sleep_point(uint8_t since, uint8_t mag)
+{
+}
+
+static void init_sleep(void)
+{
+}
+
+static void log_data_point(uint64_t when, uint8_t mag)
+{
+ uint8_t since = (uint8_t)(when-now);
+ if (when-now > 255) since = 255;
+ now = when;
+ if (monitor_mode == mmSteps)
+ log_step_point(since, mag);
+ else
+ log_sleep_point(since, mag);
+}
+
+static void accel_handler(AccelData *data, uint32_t num_samples)
+{
+ if (monitor_mode != last_monitor_mode) {
+ if (monitor_mode == mmSteps)
+ init_steps();
+ else
+ init_sleep();
+ last_monitor_mode = monitor_mode;
+ /* Shift "now" to the timestamp */
+ now = data->timestamp;
+ }
+ for (uint32_t n = 0; n < num_samples; ++n, ++data) {
+ if (data->did_vibrate) continue;
+ int16_t x = data->x, y = data->y, z = data->z;
+ int8_t tx = strunc(x), ty = strunc(y), tz = strunc(z);
+ uint32_t tmag = mag(tx,ty,tz);
+ uint8_t smooth = (uint8_t)runiir(tmag);
+ log_data_point(data->timestamp, smooth);
+ }
+}
+
+void init_activity_monitor(void)
+{
+ /* TODO: Data logging */
+ accel_data_service_subscribe(10, accel_handler);
+ accel_service_set_sampling_rate(ACCEL_SAMPLING_10HZ);
+}
+
+void fini_activity_monitor(void)
+{
+ accel_data_service_unsubscribe();
+ /* TODO: Data logging */
+}
diff --git a/watchapp/src/actimon.h b/watchapp/src/actimon.h
new file mode 100644
index 0000000..fe4dcf2
--- /dev/null
+++ b/watchapp/src/actimon.h
@@ -0,0 +1,19 @@
+#ifndef ACTIMON_H
+#define ACTIMON_H 1
+
+#include <stdint.h>
+
+/* Activity monitor API */
+
+typedef enum {
+ mmSteps,
+ mmSleep
+} monitor_mode_e;
+
+extern monitor_mode_e monitor_mode;
+extern uint32_t step_total_today;
+
+void init_activity_monitor(void);
+void fini_activity_monitor(void);
+
+#endif
diff --git a/watchapp/src/mainmenu.c b/watchapp/src/mainmenu.c
new file mode 100644
index 0000000..8a2a93b
--- /dev/null
+++ b/watchapp/src/mainmenu.c
@@ -0,0 +1,64 @@
+#include <pebble.h>
+
+static Window *window;
+
+static SimpleMenuLayer *mainmenu_layer;
+
+static SimpleMenuSection menu_sections[1];
+
+static SimpleMenuItem first_menu_items[1];
+
+static void handle_exit(int index, void *context)
+{
+ /* Pop the menu */
+ window_stack_pop(true);
+ /* And pop the main window, causing app to exit */
+ window_stack_pop(true);
+}
+
+static void window_load(Window *window)
+{
+ first_menu_items[0] = (SimpleMenuItem){
+ .title = "Exit App",
+ .callback = handle_exit,
+ };
+
+ menu_sections[0] = (SimpleMenuSection){
+ .num_items = 1,
+ .items = first_menu_items,
+ };
+
+ Layer *window_layer = window_get_root_layer(window);
+ GRect bounds = layer_get_frame(window_layer);
+
+ mainmenu_layer = simple_menu_layer_create(bounds, window, menu_sections, 1, NULL);
+
+ layer_add_child(window_layer, simple_menu_layer_get_layer(mainmenu_layer));
+}
+
+static void window_unload(Window *window)
+{
+ simple_menu_layer_destroy(mainmenu_layer);
+}
+
+void create_menu(void)
+{
+ window = window_create();
+
+ // Setup the window handlers
+ window_set_window_handlers(window, (WindowHandlers) {
+ .load = window_load,
+ .unload = window_unload,
+ });
+}
+
+void push_menu(void)
+{
+ window_stack_push(window, true /* Animated */);
+}
+
+void destroy_menu(void)
+{
+ window_destroy(window);
+}
+
diff --git a/watchapp/src/mainmenu.h b/watchapp/src/mainmenu.h
new file mode 100644
index 0000000..dd44654
--- /dev/null
+++ b/watchapp/src/mainmenu.h
@@ -0,0 +1,10 @@
+#ifndef MAINMENU_H
+#define MAINMENU_H
+
+#include <stdbool.h>
+
+void create_menu(void);
+void push_menu(void);
+void destroy_menu(void);
+
+#endif
diff --git a/watchapp/src/watchapp.c b/watchapp/src/watchapp.c
index 516f7c8..995ab80 100644
--- a/watchapp/src/watchapp.c
+++ b/watchapp/src/watchapp.c
@@ -1,41 +1,117 @@
#include <pebble.h>
+#include "mainmenu.h"
+#include "actimon.h"
static Window *window;
-static TextLayer *text_layer;
+static TextLayer *date_layer;
+static TextLayer *time_layer;
+static Layer *line_layer;
+static TextLayer *step_layer;
-static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
- text_layer_set_text(text_layer, "Select");
+static void draw_line(Layer *layer, GContext *ctx)
+{
+ graphics_context_set_stroke_color(ctx, GColorWhite);
+ graphics_draw_line(ctx, GPoint(0, 0), GPoint(144-8, 0));
+ graphics_draw_line(ctx, GPoint(0, 1), GPoint(144-8, 1));
}
-static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
- text_layer_set_text(text_layer, "Up");
+static void handle_second_tick(struct tm* tick_time, TimeUnits units_changed)
+{
+ static char time_text[] = "00:00";
+ static char date_text[] = "September 30 ";
+ static char step_text[] = "999999 steps";
+ strftime(time_text, sizeof(time_text), "%k:%M", tick_time);
+ strftime(date_text, sizeof(date_text), "%B %d", tick_time);
+ if (monitor_mode == mmSteps)
+ snprintf(step_text, sizeof(step_text), "%lu steps", step_total_today);
+ else
+ snprintf(step_text, sizeof(step_text), "Sleeping");
+ text_layer_set_text(time_layer, time_text);
+ text_layer_set_text(date_layer, date_text);
+ text_layer_set_text(step_layer, step_text);
}
-static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
- text_layer_set_text(text_layer, "Down");
+static void select_click_handler(ClickRecognizerRef recognizer, void *context)
+{
+ push_menu();
}
-static void click_config_provider(void *context) {
+static void up_click_handler(ClickRecognizerRef recognizer, void *context)
+{
+}
+
+static void down_click_handler(ClickRecognizerRef recognizer, void *context)
+{
+}
+
+static void back_click_handler(ClickRecognizerRef recognizer, void *context)
+{
+}
+
+static void click_config_provider(void *context)
+{
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
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_BACK, back_click_handler);
}
-static void window_load(Window *window) {
- Layer *window_layer = window_get_root_layer(window);
- GRect bounds = layer_get_bounds(window_layer);
-
- text_layer = text_layer_create((GRect) { .origin = { 0, 72 }, .size = { bounds.size.w, 20 } });
- text_layer_set_text(text_layer, "Press a button");
- text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
- layer_add_child(window_layer, text_layer_get_layer(text_layer));
+static void window_load(Window *window)
+{
+ window_set_fullscreen(window, true);
+ window_set_background_color(window, GColorBlack);
+ GRect full = layer_get_bounds(window_get_root_layer(window));
+ APP_LOG(APP_LOG_LEVEL_DEBUG, "{.origin={.x=%d,.y=%d},.size={.w=%d,.h=%d}}\n",
+ full.origin.x, full.origin.y, full.size.w, full.size.h);
+ full.size.h += 16; /* Account for status bar no longer shown */
+ layer_set_bounds(window_get_root_layer(window), full);
+ // Init the text layer used to show the time
+ time_layer = text_layer_create(GRect(4, 168-49-8, 144-4 /* width */, 49 /* height */));
+ text_layer_set_text_color(time_layer, GColorWhite);
+ text_layer_set_background_color(time_layer, GColorClear);
+ text_layer_set_font(time_layer, fonts_get_system_font(FONT_KEY_ROBOTO_BOLD_SUBSET_49));
+
+ // Init the text layer used to show the date
+ date_layer = text_layer_create(GRect(4, 168-49-28-4, 144-4, 28));
+ text_layer_set_text_color(date_layer, GColorWhite);
+ text_layer_set_background_color(date_layer, GColorClear);
+ text_layer_set_font(date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD));
+
+ // Init the text layer used to show steps
+ step_layer = text_layer_create(GRect(4, 56, 144-4, 28));
+ text_layer_set_text_color(step_layer, GColorWhite);
+ text_layer_set_background_color(step_layer, GColorClear);
+ text_layer_set_font(date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD));
+
+ // Ensures time is displayed immediately (will break if NULL tick event accessed).
+ // (This is why it's a good idea to have a separate routine to do the update itself.)
+ time_t now = time(NULL);
+ struct tm *current_time = localtime(&now);
+ handle_second_tick(current_time, SECOND_UNIT);
+ tick_timer_service_subscribe(SECOND_UNIT, &handle_second_tick);
+
+ // Finally create a simple white line
+ line_layer = layer_create(GRect(4, 168-49-1, 144-8, 2));
+ layer_set_update_proc(line_layer, draw_line);
+
+ // And stuff the layers into the window
+ layer_add_child(window_get_root_layer(window), text_layer_get_layer(time_layer));
+ layer_add_child(window_get_root_layer(window), text_layer_get_layer(date_layer));
+ layer_add_child(window_get_root_layer(window), text_layer_get_layer(step_layer));
+ layer_add_child(window_get_root_layer(window), line_layer);
}
-static void window_unload(Window *window) {
- text_layer_destroy(text_layer);
+static void window_unload(Window *window)
+{
+ tick_timer_service_unsubscribe();
+ text_layer_destroy(time_layer);
+ text_layer_destroy(date_layer);
+ text_layer_destroy(step_layer);
+ layer_destroy(line_layer);
}
-static void init(void) {
+static void init(void)
+{
window = window_create();
window_set_click_config_provider(window, click_config_provider);
window_set_window_handlers(window, (WindowHandlers) {
@@ -46,15 +122,19 @@ static void init(void) {
window_stack_push(window, animated);
}
-static void deinit(void) {
+static void deinit(void)
+{
window_destroy(window);
}
-int main(void) {
+int main(void)
+{
init();
-
+ create_menu();
+ init_activity_monitor();
APP_LOG(APP_LOG_LEVEL_DEBUG, "Done initializing, pushed window: %p", window);
-
app_event_loop();
+ fini_activity_monitor();
+ destroy_menu();
deinit();
}