From 9f574f048640396c5263517432b5f935a8fed249 Mon Sep 17 00:00:00 2001 From: anschrammh Date: Sun, 26 Mar 2023 23:13:24 +0200 Subject: [PATCH] Added new features and graphics to the watch face : Added a battery icon which is blinking when the battery is low Addes a charging and charged battery icon Added a step count counter widget with icon and count Added a bluetooth status icon showing when ble is enabled,disabled and when a device is connected Added the possibility to hide the hour and minute hands with a long press on the screen, this enables to see information which might be hidden by the hands ... --- src/W800_SDK_v1.00.10/app/gfx/watch_face.c | 245 ++++++++++++++++++--- src/W800_SDK_v1.00.10/app/gfx/watch_face.h | 57 ++++- 2 files changed, 270 insertions(+), 32 deletions(-) diff --git a/src/W800_SDK_v1.00.10/app/gfx/watch_face.c b/src/W800_SDK_v1.00.10/app/gfx/watch_face.c index 8e508a9..2efebdd 100644 --- a/src/W800_SDK_v1.00.10/app/gfx/watch_face.c +++ b/src/W800_SDK_v1.00.10/app/gfx/watch_face.c @@ -4,6 +4,27 @@ #include #include "app_log.h" +LV_IMG_DECLARE(battery_low_icon) +LV_IMG_DECLARE(battery_charging_icon) +LV_IMG_DECLARE(battery_charged_icon) + +static void _set_bluetooth_indicator(WatchFace_t * const watchFace) +{ + switch(watchFace->bluetoothIndicator.bluetoothState) + { + case BLUETOOTH_STATE_ON: + lv_obj_set_style_img_recolor_opa(watchFace->bluetoothIndicator.bluetoothIcon, 185, LV_PART_MAIN); + lv_obj_clear_flag(watchFace->bluetoothIndicator.bluetoothIcon, LV_OBJ_FLAG_HIDDEN); + break; + case BLUETOOTH_STATE_CONNECTED: + lv_obj_set_style_img_recolor_opa(watchFace->bluetoothIndicator.bluetoothIcon, 0, LV_PART_MAIN); + lv_obj_clear_flag(watchFace->bluetoothIndicator.bluetoothIcon, LV_OBJ_FLAG_HIDDEN); + break; + default: + lv_obj_add_flag(watchFace->bluetoothIndicator.bluetoothIcon, LV_OBJ_FLAG_HIDDEN); + } +} + static void gesture_event_cb(lv_event_t * e) { WatchFace_t *watchFace = e->user_data; @@ -17,6 +38,7 @@ static void gesture_event_cb(lv_event_t * e) case LV_DIR_RIGHT: LV_LOG_USER("GESTURE : RIGHT"); // We delete the timer + lv_timer_del(watchFace->batteryIndicator.lowBatteryAnimationTimer); lv_timer_del(watchFace->handAnimationTimer); // We create the menu screen and switch to it extern MenuScreen_t menuScreen; @@ -60,11 +82,17 @@ static void update_watch_hands_angles(WatchFace_t * const watchFace, uint8_t inc //Don't forget to update the day date window sprintf(watchFace->dateWindow.dateWindowText, "%s%d", watchFace->dateTime.tm_mday < 10 ? " " : "", watchFace->dateTime.tm_mday); - lv_obj_invalidate(watchFace->dateWindow.dateWindowWidget); + lv_label_set_text_static(watchFace->dateWindow.dateWindowWidget, watchFace->dateWindow.dateWindowText); + APP_LOG_DEBUG("Syncing time"); if(watchFace->batteryIndicatorCb) - watch_face_set_battery_indicator(watchFace, watchFace->batteryIndicatorCb()); + { + uint8_t levelInPercent = 0; + BatteryState_e batteryState = BATTERY_STATE_DISCHARGING; + watchFace->batteryIndicatorCb(&levelInPercent, &batteryState); + watch_face_set_battery_indicator(watchFace, levelInPercent, batteryState); + } } else { @@ -93,6 +121,57 @@ static void hand_timer_anim_cb(lv_timer_t *timer) update_watch_hands_angles(watchFace, 12); } +static void battery_timer_anim_cb(lv_timer_t *timer) +{ + WatchFace_t *watchFace = timer->user_data; + + if(lv_obj_has_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN)) + { + lv_obj_clear_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN); + } + else + { + lv_obj_add_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN); + } +} + +static void set_battery_state_icon(WatchFace_t * const watchFace) +{ + switch(watchFace->batteryIndicator.batteryState) + { + case BATTERY_STATE_CHARGING: + lv_timer_pause(watchFace->batteryIndicator.lowBatteryAnimationTimer); + lv_obj_clear_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN); + lv_img_set_src(watchFace->batteryIndicator.batteryIcon, &battery_charging_icon); + break; + case BATTERY_STATE_CHARGED: + lv_timer_pause(watchFace->batteryIndicator.lowBatteryAnimationTimer); + lv_obj_clear_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN); + lv_img_set_src(watchFace->batteryIndicator.batteryIcon, &battery_charged_icon); + break; + default: + lv_obj_add_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN); + lv_img_set_src(watchFace->batteryIndicator.batteryIcon, &battery_low_icon); + break; + } +} + +static void hide_hour_and_minutes_hand_cb(lv_event_t *e) +{ + WatchFace_t *watchFace = e->user_data; + + if(lv_obj_has_flag(watchFace->hourHand.handImg, LV_OBJ_FLAG_HIDDEN)) + { + lv_obj_clear_flag(watchFace->hourHand.handImg, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(watchFace->minuteHand.handImg, LV_OBJ_FLAG_HIDDEN); + } + else + { + lv_obj_add_flag(watchFace->hourHand.handImg, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(watchFace->minuteHand.handImg, LV_OBJ_FLAG_HIDDEN); + } +} + void watch_face_init(WatchFace_t * const watchFace) { if(!watchFace) @@ -101,6 +180,7 @@ void watch_face_init(WatchFace_t * const watchFace) return; } memset(watchFace, 0, sizeof(WatchFace_t)); + strcpy(watchFace->stepCounter.text, "0"); } void watch_face_register_date_time_cb(WatchFace_t * const watchFace, DateTimeCb_t dateTimeCb) @@ -139,7 +219,8 @@ void watch_face_create(WatchFace_t * const watchFace) LV_IMG_DECLARE(watch_casio_minute_hand_asset) LV_IMG_DECLARE(watch_casio_second_hand_asset) LV_IMG_DECLARE(watch_casio_medium_hand_asset) - LV_IMG_DECLARE(watch_casio_small_hand_asset) + //LV_IMG_DECLARE(watch_casio_small_hand_asset) + LV_IMG_DECLARE(bluetooth_icon) //We create our parent screen : if(watchFace->display) @@ -152,30 +233,31 @@ void watch_face_create(WatchFace_t * const watchFace) watchFace->display = lv_img_create(NULL); lv_img_set_src(watchFace->display, &watch_casio_face_asset); lv_obj_set_style_bg_color(watchFace->display, lv_color_black(), LV_PART_MAIN); + lv_obj_add_event_cb(watchFace->display, &(hide_hour_and_minutes_hand_cb), LV_EVENT_LONG_PRESSED, watchFace); //We load our other assets : - lv_obj_t *smallHandImg = lv_img_create(watchFace->display); + /*lv_obj_t *smallHandImg = lv_img_create(watchFace->display); lv_img_set_src(smallHandImg, &watch_casio_small_hand_asset); lv_obj_set_pos(smallHandImg, 69, 98); - lv_img_set_pivot(smallHandImg, 4, 20); + lv_img_set_pivot(smallHandImg, 4, 20);*/ //Battery arc is created here - if(watchFace->batteryIndicator.battery_arc) + if(watchFace->batteryIndicator.batteryArc) { - LV_LOG_ERROR("battery_arc should be NULL here !"); - lv_obj_del(watchFace->batteryIndicator.battery_arc); - watchFace->batteryIndicator.battery_arc = NULL; + LV_LOG_ERROR("batteryArc should be NULL here !"); + lv_obj_del(watchFace->batteryIndicator.batteryArc); + watchFace->batteryIndicator.batteryArc = NULL; } - watchFace->batteryIndicator.battery_arc = lv_arc_create(watchFace->display); - lv_obj_remove_style(watchFace->batteryIndicator.battery_arc, NULL, LV_PART_KNOB); - lv_obj_clear_flag(watchFace->batteryIndicator.battery_arc, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(watchFace->batteryIndicator.battery_arc, 60, 60); - lv_obj_align(watchFace->batteryIndicator.battery_arc, LV_ALIGN_CENTER, -1, 45); - lv_obj_set_style_arc_width(watchFace->batteryIndicator.battery_arc, 5, LV_PART_INDICATOR); - lv_obj_set_style_arc_width(watchFace->batteryIndicator.battery_arc, 0, LV_PART_MAIN); - lv_obj_set_style_arc_color(watchFace->batteryIndicator.battery_arc, lv_color_make(228, 233, 236), LV_PART_INDICATOR); - lv_arc_set_value(watchFace->batteryIndicator.battery_arc, 100); + watchFace->batteryIndicator.batteryArc = lv_arc_create(watchFace->display); + lv_obj_remove_style(watchFace->batteryIndicator.batteryArc, NULL, LV_PART_KNOB); + lv_obj_clear_flag(watchFace->batteryIndicator.batteryArc, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_size(watchFace->batteryIndicator.batteryArc, 60, 60); + lv_obj_align(watchFace->batteryIndicator.batteryArc, LV_ALIGN_CENTER, -1, 45); + lv_obj_set_style_arc_width(watchFace->batteryIndicator.batteryArc, 5, LV_PART_INDICATOR); + lv_obj_set_style_arc_width(watchFace->batteryIndicator.batteryArc, 0, LV_PART_MAIN); + lv_obj_set_style_arc_color(watchFace->batteryIndicator.batteryArc, lv_color_make(228, 233, 236), LV_PART_INDICATOR); + lv_arc_set_value(watchFace->batteryIndicator.batteryArc, 100); if(watchFace->batteryIndicator.label) { @@ -188,9 +270,57 @@ void watch_face_create(WatchFace_t * const watchFace) strcpy(watchFace->batteryIndicator.text, "100 %"); lv_label_set_text_static(watchFace->batteryIndicator.label, watchFace->batteryIndicator.text); lv_obj_set_style_text_color(watchFace->batteryIndicator.label, lv_color_white(), LV_PART_MAIN); - lv_obj_align_to(watchFace->batteryIndicator.label, watchFace->batteryIndicator.battery_arc, LV_ALIGN_CENTER, 0, 0); + lv_obj_align_to(watchFace->batteryIndicator.label, watchFace->batteryIndicator.batteryArc, LV_ALIGN_CENTER, 0, -5); + // Battery icon is created here + if(watchFace->batteryIndicator.batteryIcon) + { + LV_LOG_ERROR("batteryIcon should be NULL here !"); + lv_obj_del(watchFace->batteryIndicator.batteryIcon); + watchFace->batteryIndicator.batteryIcon = NULL; + } + watchFace->batteryIndicator.batteryIcon = lv_img_create(watchFace->display); + set_battery_state_icon(watchFace); + lv_img_set_zoom(watchFace->batteryIndicator.batteryIcon, 141); + lv_obj_align_to(watchFace->batteryIndicator.batteryIcon, watchFace->batteryIndicator.label, LV_ALIGN_OUT_BOTTOM_MID, 0, -9); + + if(watchFace->batteryIndicator.lowBatteryAnimationTimer) + { + LV_LOG_ERROR("battery animation timer should be NULL here !"); + lv_timer_del(watchFace->batteryIndicator.lowBatteryAnimationTimer); + watchFace->batteryIndicator.lowBatteryAnimationTimer = NULL; + } + watchFace->batteryIndicator.lowBatteryAnimationTimer = lv_timer_create(&(battery_timer_anim_cb), 500, watchFace); + lv_timer_pause(watchFace->batteryIndicator.lowBatteryAnimationTimer); + + // Bluetooth status icon is created here + if(watchFace->bluetoothIndicator.bluetoothIcon) + { + LV_LOG_ERROR("bluetoothIcon be NULL here !"); + lv_obj_del(watchFace->bluetoothIndicator.bluetoothIcon); + watchFace->bluetoothIndicator.bluetoothIcon = NULL; + } + watchFace->bluetoothIndicator.bluetoothIcon = lv_img_create(watchFace->display); + lv_img_set_src(watchFace->bluetoothIndicator.bluetoothIcon, &bluetooth_icon); + lv_img_set_zoom(watchFace->bluetoothIndicator.bluetoothIcon, 128); + lv_obj_add_flag(watchFace->bluetoothIndicator.bluetoothIcon, LV_OBJ_FLAG_HIDDEN); + lv_obj_set_style_img_recolor(watchFace->bluetoothIndicator.bluetoothIcon, lv_palette_main(LV_PALETTE_GREY), LV_PART_MAIN); + lv_obj_align_to(watchFace->bluetoothIndicator.bluetoothIcon, watchFace->batteryIndicator.batteryArc, LV_ALIGN_OUT_LEFT_BOTTOM, -9, 0); + _set_bluetooth_indicator(watchFace); + + // StepCounter label is created here + if(watchFace->stepCounter.label) + { + LV_LOG_ERROR("stepCounter should be NULL here !"); + lv_obj_del(watchFace->stepCounter.label); + watchFace->stepCounter.label = NULL; + } + + watchFace->stepCounter.label = lv_label_create(watchFace->display); + lv_label_set_text_static(watchFace->stepCounter.label, watchFace->stepCounter.text); + lv_obj_set_style_text_color(watchFace->stepCounter.label, lv_color_white(), LV_PART_MAIN); + lv_obj_set_pos(watchFace->stepCounter.label, 63, 111); if(watchFace->mediumHand24h.handImg) { @@ -278,7 +408,7 @@ void watch_face_create(WatchFace_t * const watchFace) watchFace->handAnimationTimer = lv_timer_create(&(hand_timer_anim_cb), 199, watchFace); } -void watch_face_set_battery_indicator(WatchFace_t * const watchFace, uint8_t percentage) +void watch_face_set_battery_indicator(WatchFace_t * const watchFace, uint8_t levelInPercent, BatteryState_e batteryState) { if(!watchFace) { @@ -290,18 +420,73 @@ void watch_face_set_battery_indicator(WatchFace_t * const watchFace, uint8_t per lv_color_t arc_color = lv_color_make(228, 233, 236); - if(percentage <= 10) + if(levelInPercent <= 10) arc_color = lv_color_make(228, 33, 81); - else if(percentage <= 30) + else if(levelInPercent <= 30) arc_color = lv_color_make(247, 148, 29); - else if(percentage <= 50) + else if(levelInPercent <= 50) arc_color = lv_color_make(226, 175, 58); - lv_arc_set_value(watchFace->batteryIndicator.battery_arc, percentage); - lv_obj_set_style_arc_color(watchFace->batteryIndicator.battery_arc, arc_color, LV_PART_INDICATOR); - sprintf(watchFace->batteryIndicator.text, "%u %%", percentage); + lv_arc_set_value(watchFace->batteryIndicator.batteryArc, levelInPercent); + lv_obj_set_style_arc_color(watchFace->batteryIndicator.batteryArc, arc_color, LV_PART_INDICATOR); + sprintf(watchFace->batteryIndicator.text, "%u %%", levelInPercent); lv_label_set_text_static(watchFace->batteryIndicator.label, watchFace->batteryIndicator.text); - lv_obj_align_to(watchFace->batteryIndicator.label, watchFace->batteryIndicator.battery_arc, LV_ALIGN_CENTER, 0, 0); + lv_obj_align_to(watchFace->batteryIndicator.label, watchFace->batteryIndicator.batteryArc, LV_ALIGN_CENTER, 0, -5); + + //We save the new battery state only if it's different, this allows to have a trigger when it changes : + if(watchFace->batteryIndicator.batteryState != batteryState) + { + watchFace->batteryIndicator.batteryState = batteryState; + set_battery_state_icon(watchFace); + } + + //Finally we check if it's time to show the battery low indicator by enabling it's timer + if(levelInPercent <= 10 && watchFace->batteryIndicator.batteryState == BATTERY_STATE_DISCHARGING) + { + lv_timer_resume(watchFace->batteryIndicator.lowBatteryAnimationTimer); + } + else if(watchFace->batteryIndicator.batteryState == BATTERY_STATE_DISCHARGING) + { + lv_timer_pause(watchFace->batteryIndicator.lowBatteryAnimationTimer); + lv_obj_add_flag(watchFace->batteryIndicator.batteryIcon, LV_OBJ_FLAG_HIDDEN); + } +} + +void watch_face_set_bluetooth_indicator(WatchFace_t * const watchFace, BluetoothState_e bluetoothState) +{ + if(!watchFace) + { + LV_LOG_ERROR("NULL pointer given !"); + return; + } + + if(watchFace->bluetoothIndicator.bluetoothState == bluetoothState) return; + + watchFace->bluetoothIndicator.bluetoothState = bluetoothState; + + if(!watchFace->display) return; + + _set_bluetooth_indicator(watchFace); +} + +void watch_face_set_step_count(WatchFace_t * const watchFace, uint32_t step_count) +{ + if(!watchFace) + { + LV_LOG_ERROR("NULL pointer given !"); + return; + } + + if(step_count < 1000) + sprintf(watchFace->stepCounter.text, "%u", step_count); + else if(step_count < 9996) + sprintf(watchFace->stepCounter.text, "%.2fk", step_count/1000.0); + else + sprintf(watchFace->stepCounter.text, "%.1fk", step_count/1000.0); + + if(!watchFace->display) return; + + lv_label_set_text_static(watchFace->stepCounter.label, watchFace->stepCounter.text); } void watch_face_destroy(WatchFace_t * const watchFace) @@ -319,8 +504,12 @@ void watch_face_destroy(WatchFace_t * const watchFace) watchFace->minuteHand.handImg = NULL; watchFace->secondHand.handImg = NULL; watchFace->mediumHand24h.handImg = NULL; - watchFace->batteryIndicator.battery_arc = NULL; + watchFace->batteryIndicator.batteryArc = NULL; watchFace->batteryIndicator.label = NULL; + watchFace->batteryIndicator.batteryIcon = NULL; + watchFace->batteryIndicator.lowBatteryAnimationTimer = NULL; + watchFace->stepCounter.label = NULL; + watchFace->bluetoothIndicator.bluetoothIcon = NULL; } void watch_face_force_sync(WatchFace_t *const watchFace) diff --git a/src/W800_SDK_v1.00.10/app/gfx/watch_face.h b/src/W800_SDK_v1.00.10/app/gfx/watch_face.h index 151abba..c6fae7b 100644 --- a/src/W800_SDK_v1.00.10/app/gfx/watch_face.h +++ b/src/W800_SDK_v1.00.10/app/gfx/watch_face.h @@ -4,8 +4,23 @@ #include "lvgl.h" #include +typedef enum BatteryState +{ + BATTERY_STATE_DISCHARGING = 0, + BATTERY_STATE_CHARGING, + BATTERY_STATE_CHARGED, +} BatteryState_e; + +typedef enum BluetoothState +{ + BLUETOOTH_STATE_OFF = 0, + BLUETOOTH_STATE_ON, + BLUETOOTH_STATE_CONNECTED +} BluetoothState_e; + typedef void (*DateTimeCb_t)(struct tm * const dateTime); -typedef uint8_t (*BatteryIndicatorCb_t)(void); + +typedef void (*BatteryIndicatorCb_t)(uint8_t *levelInPercent, BatteryState_e *batteryState); typedef struct DateWindow { @@ -22,10 +37,25 @@ typedef struct WatchHand typedef struct BatteryIndicator { lv_obj_t *label; - lv_obj_t *battery_arc; + lv_obj_t *batteryArc; + lv_obj_t *batteryIcon; + lv_timer_t *lowBatteryAnimationTimer; char text[7]; + BatteryState_e batteryState:2; } BatteryIndicator_t; +typedef struct BluetoothIndicator +{ + lv_obj_t *bluetoothIcon; + BluetoothState_e bluetoothState; +} BluetoothIndicator_t; + +typedef struct StepCounter +{ + lv_obj_t *label; + char text[7]; +} StepCounter_t; + /* Watch face context object */ typedef struct WatchFace { @@ -39,7 +69,8 @@ typedef struct WatchFace lv_obj_t *display; DateWindow_t dateWindow; BatteryIndicator_t batteryIndicator; - + StepCounter_t stepCounter; + BluetoothIndicator_t bluetoothIndicator; struct tm dateTime; } WatchFace_t; @@ -76,7 +107,25 @@ void watch_face_create(WatchFace_t * const watchFace); * @param watchFace a pointer to the watch face context structure. * @param percentage the value to set the indicator to in percent. */ -void watch_face_set_battery_indicator(WatchFace_t * const watchFace, uint8_t percentage); + +/** + * @brief Sets the battery level in percent as well as it's current state to draw on the watch face. + * + * @param watchFace a pointer to the watch face context structure. + * @param levelInPercent the level to set the indicator to in percent. + * @param batteryState the current state of the battery : BATTERY_STATE_DISCHARGING, BATTERY_STATE_CHARGING or BATTERY_STATE_CHARGED + */ +void watch_face_set_battery_indicator(WatchFace_t * const watchFace, uint8_t levelInPercent, BatteryState_e batteryState); + +/** + * @brief Sets the current bluetooth state to display on the watch face + * + * @param watchFace a pointer to the watch face context structure. + * @param bluetoothState the state of the bluetooth modem to show, can be : BLUETOOTH_STATE_OFF, BLUETOOTH_STATE_ON or BLUETOOTH_STATE_CONNECTED + */ +void watch_face_set_bluetooth_indicator(WatchFace_t * const watchFace, BluetoothState_e bluetoothState); + +void watch_face_set_step_count(WatchFace_t * const watchFace, uint32_t step_count); /** * @brief Forces the watch face to sync up with the RTC by calling the provided date_time_cb