Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson
2024-12-12 16:43:03 -08:00
committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
{
"uuid": "02b1f111-ef11-4e89-af3d-25f4784214fd",
"shortName": "BG App Demo",
"longName": "BG App Demo",
"companyName": "Pebble",
"versionLabel": "1.0",
"sdkVersion": "3",
"targetPlatforms": ["basalt"],
"watchapp": {
"watchface": false
},
"appKeys": {},
"resources": {
"media": [
{
"characterRegex": "[:0-9]",
"type": "font",
"name": "FONT_ROBOTO_BOLD_SUBSET_49",
"file": "fonts/Roboto-Bold.ttf"
}
]
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pebble.h"
#include <inttypes.h>
Window *window;
TextLayer *text_layer;
Layer *line_layer;
static char g_text[100];
// Return current time in ms
static uint64_t prv_ms(void) {
time_t cur_sec;
uint16_t cur_ms = time_ms(&cur_sec, NULL);
return ((uint64_t)cur_sec * 1000) + cur_ms;
}
static void steps_event_handler(uint16_t type, AppWorkerMessage *data) {
//APP_LOG(APP_LOG_LEVEL_DEBUG, "Received new worker event. type: %d, data: %d, %d, %d", (int)type, (int)data->data0,
// (int)data->data1, (int)data->data2);
if (type == 0) {
snprintf(g_text, sizeof(g_text), "%5d %5d %5d", (int)data->data0, (int)data->data1, (int)data->data2);
text_layer_set_text(text_layer, g_text);
} else if (type == 1) {
snprintf(g_text, sizeof(g_text), "BAT: %d, %d, %d", (int)data->data0, (int)data->data1, (int)data->data2);
text_layer_set_text(text_layer, g_text);
}
}
static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
AppWorkerResult result = app_worker_launch();
APP_LOG(APP_LOG_LEVEL_INFO, "launch result: %d", result);
}
static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
AppWorkerMessage m;
app_worker_send_message('x', &m);
APP_LOG(APP_LOG_LEVEL_INFO, "crashing worker");
}
static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
AppWorkerResult result = app_worker_kill();
APP_LOG(APP_LOG_LEVEL_INFO, "kill result: %d", result);
}
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);
}
static uint32_t s_seconds_count;
void handle_second_tick(struct tm *tick_time, TimeUnits units_changed) {
bool running = app_worker_is_running();
if (false) {
const char* status = "not";
if (running) {
status = "is";
}
APP_LOG(APP_LOG_LEVEL_INFO, "worker %s running", status);
}
s_seconds_count++;
if ((s_seconds_count % 5) == 0) {
int value = persist_read_int(42);
// APP_LOG(APP_LOG_LEVEL_INFO, "Updating persist value from %d to %d", value, value + 1);
persist_write_int(42, value + 1);
}
}
static void health_event_handler(HealthEventType event, void *context) {
APP_LOG(APP_LOG_LEVEL_INFO, "app: Got health event update. event_id: %"PRIu32"",
(uint32_t) event);
if (event == HealthEventMovementUpdate) {
HealthValue steps = health_service_sum_today(HealthMetricStepCount);
APP_LOG(APP_LOG_LEVEL_INFO, "app: movement event, steps: %"PRIu32"",
(uint32_t)steps);
// Test getting historical steps
time_t day_start = time_start_of_today();
for (int i = 0; i < 7; i++) {
steps = health_service_sum(HealthMetricStepCount, day_start, day_start + SECONDS_PER_DAY);
APP_LOG(APP_LOG_LEVEL_INFO, "%d days ago steps: %d", i, (int)steps);
day_start -= SECONDS_PER_DAY;
}
// Test getting steps for part of a day
day_start = time_start_of_today();
time_t seconds_today_so_far = time(NULL) - day_start;
steps = health_service_sum(HealthMetricStepCount, day_start,
day_start + (seconds_today_so_far / 2));
APP_LOG(APP_LOG_LEVEL_INFO, "steps 1st half of today: %d", (int)steps);
steps = health_service_sum(HealthMetricStepCount, day_start - (SECONDS_PER_DAY / 2), day_start);
APP_LOG(APP_LOG_LEVEL_INFO, "steps 2nd half of yesterday: %d", (int)steps);
// Test the get_minute_history call
const int minute_data_len = 10;
HealthMinuteData minute_data[minute_data_len];
uint32_t num_records = minute_data_len;
time_t utc_start = time(NULL) - 60 * 60 * 24; // All records since 1 day ago
time_t utc_end = time(NULL);
uint64_t start_ms = prv_ms();
health_service_get_minute_history(minute_data, num_records, &utc_start, &utc_end);
uint64_t elapsed_ms = prv_ms() - start_ms;
int num_records_returned = (utc_end - utc_start) / SECONDS_PER_MINUTE;
APP_LOG(APP_LOG_LEVEL_INFO, "app: Retrieved %d minute records in %"PRIu32" ms:",
num_records_returned, (uint32_t)elapsed_ms);
for (int i = 0; i < num_records_returned; i++) {
APP_LOG(APP_LOG_LEVEL_INFO, " steps: %"PRIu8", orient: 0x%"PRIx8", vmc: %"PRIu16", "
"light: %d, valid: %d", minute_data[i].steps, minute_data[i].orientation,
minute_data[i].vmc, (int)minute_data[i].light, (int)(!minute_data[i].is_invalid));
}
} else if (event == HealthEventSleepUpdate) {
HealthValue total_sleep = health_service_sum_today(HealthMetricSleepSeconds);
HealthValue restful_sleep = health_service_sum_today(HealthMetricSleepRestfulSeconds);
APP_LOG(APP_LOG_LEVEL_INFO, "app: New sleep event: total: %"PRIu32", restful: %"PRIu32" ",
total_sleep / SECONDS_PER_MINUTE, restful_sleep / SECONDS_PER_MINUTE);
}
}
void handle_deinit(void) {
tick_timer_service_unsubscribe();
health_service_events_unsubscribe();
}
void handle_init(void) {
window = window_create();
window_set_click_config_provider(window, click_config_provider);
window_stack_push(window, true /* Animated */);
window_set_background_color(window, GColorBlack);
Layer *window_layer = window_get_root_layer(window);
text_layer = text_layer_create(GRect(7, 40, 144-7, 168-40));
text_layer_set_text_color(text_layer, GColorWhite);
text_layer_set_background_color(text_layer, GColorClear);
text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24));
layer_add_child(window_layer, text_layer_get_layer(text_layer));
text_layer_set_text(text_layer, "? ? ?");
// Subscribe to mesages published by the worker
app_worker_message_subscribe(steps_event_handler);
// Subscribe to second ticks
tick_timer_service_subscribe(SECOND_UNIT, handle_second_tick);
// Launch the worker
AppWorkerResult result = app_worker_launch();
APP_LOG(APP_LOG_LEVEL_INFO, "launch result: %d", result);
// Subscribe to health service
health_service_events_subscribe(health_event_handler, NULL);
}
int main(void) {
handle_init();
app_event_loop();
handle_deinit();
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <pebble_worker.h>
#include <inttypes.h>
#define BREAKPOINT __asm("bkpt")
#define ACCEL_BATCH_SIZE 10
#define PERSIST_WRITE_PERIOD_MS 1000
// -------------------------------------------------------------------------------------------------
static void prv_assert(bool condition, const char* msg) {
if (!condition) {
APP_LOG(APP_LOG_LEVEL_ERROR, msg);
// Force an exception
typedef void (*FuncPtr)(void);
FuncPtr bad_func = NULL;
bad_func();
}
}
// -----------------------------------------------------------------------------------------------
void handle_accel(AccelRawData *accel_data, uint32_t num_samples, uint64_t timestamp) {
// Display data
//for (uint32_t i=0; i<num_samples; i++) {
// APP_LOG(APP_LOG_LEVEL_INFO, "Got accel data: %d, %d, %d", accel_data[i].x, accel_data[i].y, accel_data[i].z);
//}
// Publish new steps count
AppWorkerMessage steps_data = {
.data0 = accel_data[0].x,
.data1 = accel_data[0].y,
.data2 = accel_data[0].z,
};
app_worker_send_message(0 /*type*/, &steps_data);
}
// -----------------------------------------------------------------------------------------------
static void update_persist_callback(void* context) {
int value = persist_read_int(42);
// APP_LOG(APP_LOG_LEVEL_INFO, "Updating persist value from %d to %d", value, value + 1);
persist_write_int(42, value + 1);
app_timer_register(PERSIST_WRITE_PERIOD_MS /*ms*/, update_persist_callback, NULL);
}
// -----------------------------------------------------------------------------------------------
static void battery_state_handler(BatteryChargeState charge) {
APP_LOG(APP_LOG_LEVEL_INFO, "got battery state service update");
APP_LOG(APP_LOG_LEVEL_INFO, "percent: %d, is_charging: %d, is_plugged: %d", charge.charge_percent,
charge.is_charging, charge.is_plugged);
AppWorkerMessage battery_data = {
.data0 = charge.charge_percent,
.data1 = charge.is_charging,
.data2 = charge.is_plugged,
};
app_worker_send_message(1 /*type*/, &battery_data);
}
// -----------------------------------------------------------------------------------------------
static void connection_handler(bool connected) {
APP_LOG(APP_LOG_LEVEL_INFO, "got phone connection update");
APP_LOG(APP_LOG_LEVEL_INFO, "connected: %d", connected);
}
// -----------------------------------------------------------------------------------------------
static void tick_timer_handler(struct tm *tick_time, TimeUnits units_changed) {
APP_LOG(APP_LOG_LEVEL_INFO, "got tick timer update");
}
// -----------------------------------------------------------------------------------------------
static void worker_message_handler(uint16_t type, AppWorkerMessage *data) {
if (type == 'x') {
prv_assert(0, "crashing");
}
}
// -----------------------------------------------------------------------------------------------
static void health_event_handler(HealthEventType event, void *context) {
APP_LOG(APP_LOG_LEVEL_INFO, "worker: Got health event update. event_id: %"PRIu32"",
(uint32_t) event);
if (event == HealthEventMovementUpdate) {
HealthValue steps = health_service_sum_today(HealthMetricStepCount);
APP_LOG(APP_LOG_LEVEL_INFO, "worker: movement event, steps: %"PRIu32"",
(uint32_t)steps);
} else if (event == HealthEventSleepUpdate) {
HealthValue total_sleep = health_service_sum_today(HealthMetricSleepSeconds);
HealthValue restful_sleep = health_service_sum_today(HealthMetricSleepRestfulSeconds);
APP_LOG(APP_LOG_LEVEL_INFO, "worker: New sleep event: total: %"PRIu32", restful: %"PRIu32" ",
total_sleep / SECONDS_PER_MINUTE, restful_sleep / SECONDS_PER_MINUTE);
}
}
// -----------------------------------------------------------------------------------------------
int main(void) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "initializing...");
accel_raw_data_service_subscribe(ACCEL_BATCH_SIZE, handle_accel);
accel_service_set_sampling_rate(ACCEL_SAMPLING_10HZ);
app_timer_register(PERSIST_WRITE_PERIOD_MS /*ms*/, update_persist_callback, NULL);
battery_state_service_subscribe(battery_state_handler);
ConnectionHandlers conn_handlers = {
.pebble_app_connection_handler = connection_handler
};
connection_service_subscribe(conn_handlers);
tick_timer_service_subscribe(MINUTE_UNIT, tick_timer_handler);
app_worker_message_subscribe(worker_message_handler);
// Subscribe to health service
// health_service_events_subscribe(health_event_handler, NULL);
worker_event_loop();
accel_data_service_unsubscribe();
health_service_events_unsubscribe();
}

View File

@@ -0,0 +1,41 @@
#
# This file is the default set of rules to compile a Pebble project.
#
# Feel free to customize this to your needs.
#
import os.path
top = '.'
out = 'build'
def options(ctx):
ctx.load('pebble_sdk')
def configure(ctx):
ctx.load('pebble_sdk')
def build(ctx):
ctx.load('pebble_sdk')
build_worker = os.path.exists('worker_src')
binaries = []
for p in ctx.env.TARGET_PLATFORMS:
ctx.set_env(ctx.all_envs[p])
ctx.set_group(ctx.env.PLATFORM_NAME)
app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'),
target=app_elf)
if build_worker:
worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR)
binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf})
ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'),
target=worker_elf)
else:
binaries.append({'platform': p, 'app_elf': app_elf})
ctx.set_group('bundle')
ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js'))