static void do_buzz(struct tm *time) { // Stop if buzzing is off if (buzz_freq == 0) { return; } int hour = time->tm_hour; int min = time->tm_min; int day = time->tm_wday; if (min == 0) min = 60; // Stop if not on for the day if (buzz_on_days[day] == 0) return; APP_LOG(APP_LOG_LEVEL_DEBUG, "about to check if within time range"); // Stop if not within time range if ((hour == (buzz_start-1) && (min+buzz_offset != 60)) || hour < (buzz_start-1)) return; if ((hour == buzz_end && buzz_offset != 0) || hour > buzz_end) return; // Stop if not at offset int buzz_min = 60; if (buzz_freq == 1) buzz_min = 30; APP_LOG(APP_LOG_LEVEL_DEBUG, "about to check offset"); if ((min+buzz_offset) % buzz_min != 0) return; if (min+buzz_offset == 60) { // Triple buzz on the hour APP_LOG(APP_LOG_LEVEL_DEBUG, "should triple buzz"); vibes_enqueue_custom_pattern(triple_vibe_pattern); } else { APP_LOG(APP_LOG_LEVEL_DEBUG, "should buzz"); vibes_enqueue_custom_pattern(double_vibe_pattern); } }
void chrono_start_stop_handler(ClickRecognizerRef recognizer, Window *window) { PblTm time; int seconds; get_time(&time); seconds = get_time_seconds(&time); // The start/stop button was pressed. if (chrono_running) { // If the chronograph is currently running, this means to stop (or // pause). chrono_hold_seconds = seconds - chrono_start_seconds; chrono_running = false; chrono_lap_paused = false; vibes_enqueue_custom_pattern(tap); update_hands(&time); // We change the click config provider according to the chrono run // state. When the chrono is stopped, we listen for a different // set of buttons than when it is started. window_set_click_config_provider(window, (ClickConfigProvider)stopped_click_config_provider); } else { // If the chronograph is not currently running, this means to // start, from the currently showing Chronograph time. chrono_start_seconds = seconds - chrono_hold_seconds; chrono_running = true; vibes_enqueue_custom_pattern(tap); update_hands(&time); window_set_click_config_provider(window, (ClickConfigProvider)started_click_config_provider); } }
void chrono_lap_button() { unsigned int ms; ms = get_time_ms(); if (chrono_data.lap_paused) { // If we were already paused, this resumes the motion, jumping // ahead to the currently elapsed time. chrono_data.lap_paused = false; vibes_enqueue_custom_pattern(tap); update_hands(NULL); } else { // If we were not already paused, this pauses the hands here (but // does not stop the timer). unsigned int lap_ms = ms - chrono_data.start_ms; record_chrono_lap(lap_ms); if (!chrono_digital_window_showing) { // Actually, we only pause the hands if we're not looking at the // digital timer. chrono_data.hold_ms = lap_ms; chrono_data.lap_paused = true; } vibes_enqueue_custom_pattern(tap); update_hands(NULL); } }
/** Tick handler. Called every second. Also called on the minute for "heartbeat" working reminders. */ void pomOnTick(struct tm *tick_time, TimeUnits units_changed) { if (app.timer.state == PomStateReady) return; app.ticksRemaining--; // check for time up // Note: because this returns, you have an extra second to see the change over. feature, not a bug. if (app.ticksRemaining < 0) { if (app.timer.state == PomStateWorking) { // you finished the pomodoro! congrats! app.timer.completedPoms++; if (app.timer.lastPomHour < 6 && tick_time->tm_hour >= 6) { app.timer.completedPoms = 0; } app.timer.lastPomHour = tick_time->tm_hour; vibes_short_pulse(); light_enable_interaction(); pomSetState(PomStateResting); return; } else if (app.timer.state == PomStateResting) { // time to start another pomodoro. vibes_enqueue_custom_pattern(VIBRATE_DIT_DIT_DAH); light_enable_interaction(); pomSetState(app.settings.autoContinue? PomStateWorking : PomStateReady); return; } } bool isWorking = (app.timer.state == PomStateWorking); // bool isResting = (app.timer.state == PomStateResting); // heartbeat if (isWorking && app.settings.vibrateWhileWorking && (app.ticksRemaining % app.settings.vibrateTicks) == 0) { vibes_enqueue_custom_pattern(VIBRATE_MINIMAL); } // TODO resize inverter /* float pctRemaining = (app.ticksRemaining + 0.0) / app.totalTicks; GRect inverterFrame = GRect(0, 0, windowSize.w, 0); if (isWorking) { inverterFrame.size.h = (1.0 - pctRemaining) * windowSize.h; } else if (isResting) { inverterFrame.size.h = pctRemaining * windowSize.h; } layer_set_frame(inverter_layer_get_layer(app.inverterLayer), inverterFrame); */ // set timer text formatTime(gTimeString, app.ticksRemaining); text_layer_set_text(app.timeTextLayer, gTimeString); // redraw! layer_mark_dirty(window_get_root_layer(app.mainWindow)); }
static void update_bluetooth(bool connected) { if (bluetooth_connection_service_peek()) { vibes_enqueue_custom_pattern(custom_pattern_connected); text_layer_set_text(bluetoothstatus_layer,"Phone is connected!"); APP_LOG(APP_LOG_LEVEL_INFO, "Phone is connected!"); } else { vibes_enqueue_custom_pattern(custom_pattern_notconnected); text_layer_set_text(bluetoothstatus_layer, "Phone is not connected!"); APP_LOG(APP_LOG_LEVEL_INFO, "Phone is not connected!"); } }
static void alert_handler(uint8_t alertValue) { APP_LOG(APP_LOG_LEVEL_DEBUG, "Alert code: %d", alertValue); switch(alertValue){ //No alert case 0: break; //Normal (new data, in range, trend okay) case 1: vibes_double_pulse(); break; //Low case 2: ; VibePattern lowpat = { .durations = low, .num_segments = ARRAY_LENGTH(low), }; vibes_enqueue_custom_pattern(lowpat); break; //High case 3: ; VibePattern highpat = { .durations = high, .num_segments = ARRAY_LENGTH(high), }; vibes_enqueue_custom_pattern(highpat); break; //Hypo //Hyper //Trend Low //Trend High //Data Alert } }
//adds or removes a tick depending on pomodoro state static void timer_callback( void* data ) { if ( pomodoro_state == IN_PROGRESS ) { size_t i = atomic_pom_count / cols; size_t j = atomic_pom_count % cols; BitmapLayer* cur_square = minute_layers[ i * 5 + j ]; //bitmap_layer_set_background_color( cur_square, GColorBlack ); //layer_mark_dirty( (Layer*) cur_square ); //bitmap_layer_get_layer( cur_square ) ); layer_set_hidden( bitmap_layer_get_layer( cur_square ), false ); atomic_pom_count++; if ( atomic_pom_count == 25 ) { static const uint32_t const segments[] = { 100, 100, 100 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), }; vibes_enqueue_custom_pattern(pat); text_layer_set_text( text_layer, "Pomodoro finished!" ); pomodoro_state = DONE; int32_t pom_count = persist_read_int( POMODORO_COUNT_PKEY ); persist_write_int( POMODORO_COUNT_PKEY, pom_count + 1 ); return; } app_timer = app_timer_register(ATOMIC_POM_DURATION, timer_callback, &atomic_pom_count); }
void beat( void ) { if( should_stop_beating( num_beats++ ) ) { // This stops beating. handle_run_click( 0, 0 ); return; } // tempo = beats per min // beat_interval = ms per beat // 60,000 ms per min // // ms_per_min / tempo = ms/min / beats/min // = ms/beat // layer_mark_dirty( &visual_beat_layer ); draw_beat = 1; // This integer division will be off by up to 1ms. beat_interval = 60000 / tempo; beat_timer = app_timer_send_event( my_ctx, beat_interval, 0 ); clear_beat_timer = app_timer_send_event( my_ctx, beat_interval / 2, 0 ); if( vibe_enabled ) { vibes_enqueue_custom_pattern( vibe_pat ); } }
static void prv_vibrate(void) { const int32_t short_pulse_duration_ms = 100; const uint32_t vibe_segments[] = { // 4 short pulses short_pulse_duration_ms, short_pulse_duration_ms * 2, short_pulse_duration_ms, short_pulse_duration_ms * 2, short_pulse_duration_ms, short_pulse_duration_ms * 2, short_pulse_duration_ms, short_pulse_duration_ms * 2, // 1 long pulse short_pulse_duration_ms * 4, short_pulse_duration_ms * 2, // 3 short pulses short_pulse_duration_ms, short_pulse_duration_ms * 2, short_pulse_duration_ms, short_pulse_duration_ms * 2, short_pulse_duration_ms, short_pulse_duration_ms * 2, }; VibePattern vibe_pattern = { .durations = vibe_segments, .num_segments = ARRAY_LENGTH(vibe_segments), }; vibes_enqueue_custom_pattern(vibe_pattern); }
void time_update( const struct tm* tick_time ) { static char buffer[] = "00:00:00 xx"; char minsec[] = "0000"; if ( clock_is_24h_style() ) { // Use 24 hour format strftime( buffer, sizeof (buffer), "%H:%M:%S", tick_time); } else { // Use 12 hour format strftime( buffer, sizeof ( buffer ), "%r", tick_time); } text_layer_set_text( Time_layer, buffer ); strftime( minsec, sizeof ( minsec ), "%M%S", tick_time); if ( strcmp( minsec, "0000" ) == 0 ) { // An hour static const uint32_t const segments[] = { 250, 250, 250 }; VibePattern pattern = { .durations = segments, .num_segments = ARRAY_LENGTH( segments ), }; vibes_enqueue_custom_pattern( pattern ); } }
void link_monitor_handle_failure(int error) { switch(error) { case 1008: //Watchapp not running //Considered a link failure break; case 1064: //APP_MSG_BUSY //These are more likely to specify a temporary error than a lost watch return; #ifdef DEBUG default: //Unrecognised failure reason. Debug. text_layer_set_text(&date_layer, itoa(error)); #endif } if(__linkStatus == LinkStatusOK) { //The link has just failed, notify the user // Vibe pattern: ON, OFF, ON, ... static const uint32_t const segments[] = { 150, 100, 150, 100, 300 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), }; vibes_enqueue_custom_pattern(pat); } __linkStatus = LinkStatusFailed; }
void link_monitor_handle_failure(int error) { switch(error) { case 1008: //Watchapp not running //Considered a link failure break; case 1064: //APP_MSG_BUSY //These are more likely to specify a temporary error than a lost watch return; case HTTP_INVALID_BRIDGE_RESPONSE + 1000: //The phone may have no internet connection, but the link should be fine return; } if(__linkStatus == LinkStatusOK && VIBRATE == 1) { //The link has just failed, notify the user // Vibe pattern: ON, OFF, ON, ... static const uint32_t const segments[] = { 150, 100, 150, 100, 300 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), }; vibes_enqueue_custom_pattern(pat); } __linkStatus = LinkStatusFailed; }
static void workout_reminder() { static const uint32_t segments[] = { 100, 300, 100, 300, 200 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), }; vibes_enqueue_custom_pattern(pat); }
void bt_connection_handler(bool connected) { bt_ok = connected; if (!bt_ok) { vibes_enqueue_custom_pattern(pat); } layer_mark_dirty(bt_layer); }
static void bt_handler(bool connected) { if (!connected && m_bBtConnected //vibrate once upon BT connection lost && (m_nVibes & MASKV_BTDC)) //only if option enabled { vibes_enqueue_custom_pattern(VIBE_PAT_BT_LOSS); } m_bBtConnected = connected; if (s_spoke_layer) layer_mark_dirty(s_spoke_layer); }
static void vibration_control(int pattern) { // 100ms static const uint32_t const vibe_test[] = { 100, 100 }; // 時報通知: 長く1回、短く1回 static const uint32_t const vibe_every_hour[] = { 500, 100, 100 }; // 30分通知: 短く2回 static const uint32_t const vibe_half_hour[] = { 100, 100, 100 }; // BT接続切断通知: 短く5回鳴動 static const uint32_t const vibe_bt_disconnect[] = { 100,100, 100,100, 100,100, 100,100, 100,100 }; // BT接続回復通知: 短く1回、長く1回 static const uint32_t const vibe_bt_connect[] = { 100, 100, 500 }; // バイブレーション設定の初期値 static int prev_pattern=VIBE_INIT; // バイブレーションステータスが直前と同じ場合は何もしない。 if ( prev_pattern == VIBE_INIT || ( prev_pattern == pattern && pattern != VIBE_BT_DISCONNECT ) ) { prev_pattern=pattern; return; } VibePattern pat = { .durations = vibe_test, .num_segments = ARRAY_LENGTH(vibe_test), }; switch(pattern) { case VIBE_EVERY_HOUR: pat.durations = vibe_every_hour; pat.num_segments = ARRAY_LENGTH(vibe_every_hour); break; case VIBE_HALF_HOUR: pat.durations = vibe_half_hour; pat.num_segments = ARRAY_LENGTH(vibe_half_hour); break; case VIBE_BT_DISCONNECT: pat.durations = vibe_bt_disconnect; pat.num_segments = ARRAY_LENGTH(vibe_bt_disconnect); break; case VIBE_BT_CONNECT: pat.durations = vibe_bt_connect; pat.num_segments = ARRAY_LENGTH(vibe_bt_connect); break; default: return; } vibes_enqueue_custom_pattern(pat); prev_pattern=pattern; }
void handle_minute_tick(AppContextRef ctx, PebbleTickEvent *t) { (void)t; (void)ctx; // Need to be static because they're used by the system later. static char time_text[] = "00:00"; static char dom_text[] = "00"; string_format_time(dom_text, sizeof(dom_text), "%e", t->tick_time); static char dow_text[] = "xxx"; string_format_time(dow_text, sizeof(dow_text), "%a", t->tick_time); static char yon_text[] = "00"; string_format_time(yon_text, sizeof(yon_text), "%y", t->tick_time); static char mon_text[] = "xxx"; string_format_time(mon_text, sizeof(mon_text), "%b", t->tick_time); char *time_format; if (clock_is_24h_style()) { time_format = "%R"; } else { time_format = "%I:%M"; } string_format_time(time_text, sizeof(time_text), time_format, t->tick_time); if (!clock_is_24h_style() && (time_text[0] == '0')) { memmove(time_text, &time_text[1], sizeof(time_text) - 1); } text_layer_set_text(&dow_layer, dow_text); text_layer_set_text(&dom_layer, dom_text); text_layer_set_text(&yon_layer, yon_text); text_layer_set_text(&mon_layer, mon_text); text_layer_set_text(&text_time_layer, time_text); text_layer_set_text_alignment(&text_time_layer, GTextAlignmentCenter); rotbmp_pair_layer_set_angle(&bitmap_container.layer, TRIG_MAX_ANGLE * get24HourAngle(t->tick_time->tm_hour, t->tick_time->tm_min)); bitmap_container.layer.layer.frame.origin.x = (144/2) - (bitmap_container.layer.layer.frame.size.w/2); bitmap_container.layer.layer.frame.origin.y = (168/2) - (bitmap_container.layer.layer.frame.size.h/2); // Vibrate Every Hour #if HOUR_VIBRATION if ((t->tick_time->tm_min==0) && (t->tick_time->tm_sec==0)) { vibes_enqueue_custom_pattern(hour_pattern); } #endif updateDayAndNightInfo(false); }
static void menu_select_callback(int index, void *ctx) { //window_stack_pop( true ); static const uint32_t const segments[] = { 100, 100, 100 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), }; vibes_enqueue_custom_pattern(pat); }
void vibrate(bool long_vibe) { // Vibe pattern: ON for 200ms, OFF for 100ms, ON for 400ms: if(long_vibe) { VibePattern pat = { .durations = long_vibe_segments, .num_segments = ARRAY_LENGTH(long_vibe_segments), }; vibes_enqueue_custom_pattern(pat); } else {
void nw_vibrate(VibePattern* vibePattern, uint16_t totalLength) { vibes_cancel(); vibes_enqueue_custom_pattern(*vibePattern); vibrating = true; app_timer_register(totalLength, vibration_stopped, NULL); }
// Called once per minute void handle_minute_tick(struct tm *tick_time, TimeUnits units_changed) { static char timeText[] = "00:00"; // Needs to be static because it's used by the system later. time_t now = time(NULL); struct tm * currentTime = localtime(&now); if (currentTime->tm_min == 0) { vibes_enqueue_custom_pattern(HOUR_VIBE_PATTERN); } else if ((currentTime->tm_min % VIBE_INTERVAL_IN_MINUTES) == 0) { vibes_enqueue_custom_pattern(PART_HOUR_INTERVAL_VIBE_PATTERN); } strftime(timeText, sizeof(timeText), "%R", currentTime); text_layer_set_text(timeLayer, timeText); }
//----------------------------------------------------------------------------------------------------------------------- void bluetooth_connection_handler(bool connected) { layer_set_hidden(bitmap_layer_get_layer(radio_layer), connected != true); if (!connected && aktBT == 1) vibes_enqueue_custom_pattern(vibe_pat_bt); aktBT = connected; }
void chrono_start_stop_handler(ClickRecognizerRef recognizer, void *context) { Window *window = (Window *)context; unsigned int ms = get_time_ms(); // The start/stop button was pressed. if (chrono_data.running) { // If the chronograph is currently running, this means to stop (or // pause). chrono_data.hold_ms = ms - chrono_data.start_ms; chrono_data.running = false; chrono_data.lap_paused = false; vibes_enqueue_custom_pattern(chrono_tap); update_hands(NULL); reset_tick_timer(); // We change the click config provider according to the chrono run // state. When the chrono is stopped, we listen for a different // set of buttons than when it is started. window_set_click_config_provider(window, &stopped_click_config_provider); if (chrono_digital_window != NULL) { window_set_click_config_provider(chrono_digital_window, &stopped_click_config_provider); } } else { // If the chronograph is not currently running, this means to // start, from the currently showing Chronograph time. chrono_data.start_ms = ms - chrono_data.hold_ms; chrono_data.running = true; #if ENABLE_SWEEP_SECONDS if (config.sweep_seconds) { if (sweep_chrono_seconds_ms < sweep_timer_ms) { sweep_timer_ms = sweep_chrono_seconds_ms; } } #endif // ENABLE_SWEEP_SECONDS vibes_enqueue_custom_pattern(chrono_tap); update_hands(NULL); reset_tick_timer(); window_set_click_config_provider(window, &started_click_config_provider); if (chrono_digital_window != NULL) { window_set_click_config_provider(chrono_digital_window, &started_click_config_provider); } } }
void select_click_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) { int which = cell_index->row; //The array that will hold the on/off vibration times uint32_t segments[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //Build the pattern (milliseconds on and off in alternating positions) for(int i = 0; i < which + 1; i++) { segments[2 * i] = 200; segments[(2 * i) + 1] = 100; } //Create a VibePattern data structure VibePattern pattern = { .durations = segments, .num_segments = 16 }; //Do the vibration pattern! vibes_enqueue_custom_pattern(pattern); psleep( 1000 ); // create window to show word_window = window_create(); window_set_window_handlers(word_window, (WindowHandlers) { .load = word_window_load, .unload = word_window_unload, }); window_stack_push(word_window, true); // create window to show app_message_register_inbox_received(inbox_received_callback); app_message_register_inbox_dropped(inbox_dropped_callback); app_message_register_outbox_failed(outbox_failed_callback); app_message_register_outbox_sent(outbox_sent_callback); //APP_LOG(APP_LOG_LEVEL_DEBUG, "Loop index now %d", 1); // Open AppMessage app_message_open( app_message_inbox_size_maximum(), app_message_outbox_size_maximum() ); // Begin dictionary DictionaryIterator *iter; app_message_outbox_begin(&iter); // Add a key-value pair dict_write_uint8(iter, 0, 0); // Send the message! app_message_outbox_send(); }
void perform_vibration(int vibrationPattern) { if (vibrationPattern == VIBRATE_BLIP) { uint32_t segments[] = { 100 }; const VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments) }; vibes_enqueue_custom_pattern(pat); } else if (vibrationPattern == VIBRATE_SHORT_BUZZ) {
// Pulse-Width Modulation Vibration // strength: pwm ranges from 0 - 10, <0 is fully off, >10 is fully on // duration: duration in milliseconds (must be > 0) void vibes_pwm(int32_t strength, uint32_t duration) { uint32_t pwm_segments[MAX_PWM_DURATION/7], pwm_seglen; if(strength < 0) { vibes_cancel(); } else if(strength > 10) { pwm_segments[0] = duration; // on for [duration] (in ms) VibePattern pwm_pat = {.durations = pwm_segments, .num_segments = 1}; vibes_enqueue_custom_pattern(pwm_pat); } else { if(duration > MAX_PWM_DURATION) duration = MAX_PWM_DURATION;
void hdlBle(bool state){ APP_LOG(APP_LOG_LEVEL_DEBUG, "BLE status change (%d)", state); main_window_update_ble(state); if(!state){ VibePattern pat = { .durations = patternLost, .num_segments = ARRAY_LENGTH(patternLost), }; vibes_enqueue_custom_pattern(pat); }
void update_connection() { if (bluetooth_connected) { } else { static const uint32_t const segments[] = { 400, 100, 400 }; VibePattern pat = { .durations = segments, .num_segments = ARRAY_LENGTH(segments), }; vibes_enqueue_custom_pattern(pat); } }
// Called once per minute void handle_minute_tick(AppContextRef ctx, PebbleTickEvent *t) { static char timeText[] = "00:00"; // Needs to be static because it's used by the system later. PblTm currentTime; get_time(¤tTime); if (currentTime.tm_min == 0) { vibes_enqueue_custom_pattern(HOUR_VIBE_PATTERN); } else if ((currentTime.tm_min % VIBE_INTERVAL_IN_MINUTES) == 0) { vibes_enqueue_custom_pattern(PART_HOUR_INTERVAL_VIBE_PATTERN); } string_format_time(timeText, sizeof(timeText), "%R", ¤tTime); text_layer_set_text(&timeLayer, timeText); }
/* * Vibrate with a random vibration scheme */ static void vibrate() { for(int i = 0; i < NUM_VIBES; i += 2) { segments[i] = rand() % 300 + 100; segments[i+1] = rand() % segments[i] + 100; } VibePattern pat = { .durations = segments, .num_segments = NUM_VIBES, }; vibes_enqueue_custom_pattern(pat); }