mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 23:14:20 +00:00
GUI: status bar rendering. Power: battery indicator. (#207)
* Menu: animation. Irukagotchi: idle image. * Power: battery, usb activity widget * Power: tune battery max voltage and clamp overshoot * get initial charge state Co-authored-by: Aleksandr Kutuzov <aku@plooks.com> Co-authored-by: aanper <mail@s3f.ru>
This commit is contained in:
parent
8aeafd8179
commit
0af239ebc0
17 changed files with 357 additions and 33 deletions
|
@ -2,6 +2,7 @@
|
|||
#include <gui/gui.h>
|
||||
#include "menu/menu.h"
|
||||
#include "applications.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
typedef struct {
|
||||
FuriApp* handler;
|
||||
|
@ -87,7 +88,9 @@ void app_loader(void* p) {
|
|||
ctx->app = &FLIPPER_APPS[i];
|
||||
|
||||
menu_item_add(
|
||||
menu, menu_item_alloc_function(FLIPPER_APPS[i].name, NULL, handle_menu, ctx));
|
||||
menu,
|
||||
menu_item_alloc_function(
|
||||
FLIPPER_APPS[i].name, assets_icons_get(A_Infrared_14), handle_menu, ctx));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -31,6 +31,7 @@ void cc1101_workaround(void* p);
|
|||
void lf_rfid_workaround(void* p);
|
||||
void nfc_task(void* p);
|
||||
void irukagotchi_task(void* p);
|
||||
void power_task(void* p);
|
||||
|
||||
const FlipperStartupApp FLIPPER_STARTUP[] = {
|
||||
#ifdef APP_DISPLAY
|
||||
|
@ -59,6 +60,10 @@ const FlipperStartupApp FLIPPER_STARTUP[] = {
|
|||
{.app = irukagotchi_task, .name = "irukagotchi_task", .libs = {1, FURI_LIB{"menu_task"}}},
|
||||
#endif
|
||||
|
||||
#ifdef APP_POWER
|
||||
{.app = power_task, .name = "power_task", .libs = {1, FURI_LIB{"gui_task"}}},
|
||||
#endif
|
||||
|
||||
#ifdef APP_CC1101
|
||||
{.app = cc1101_workaround, .name = "cc1101 workaround", .libs = {1, FURI_LIB{"gui_task"}}},
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,7 @@ APP_RELEASE ?= 0
|
|||
ifeq ($(APP_RELEASE), 1)
|
||||
APP_MENU = 1
|
||||
APP_NFC = 1
|
||||
APP_POWER = 1
|
||||
BUILD_IRDA = 1
|
||||
APP_IRUKAGOTCHI = 1
|
||||
BUILD_EXAMPLE_BLINK = 1
|
||||
|
@ -34,6 +35,13 @@ CFLAGS += -DAPP_IRUKAGOTCHI
|
|||
C_SOURCES += $(wildcard $(APP_DIR)/irukagotchi/*.c)
|
||||
endif
|
||||
|
||||
APP_POWER ?= 0
|
||||
ifeq ($(APP_POWER), 1)
|
||||
APP_GUI = 1
|
||||
CFLAGS += -DAPP_POWER
|
||||
C_SOURCES += $(wildcard $(APP_DIR)/power/*.c)
|
||||
endif
|
||||
|
||||
APP_MENU ?= 0
|
||||
ifeq ($(APP_MENU), 1)
|
||||
CFLAGS += -DAPP_MENU
|
||||
|
|
|
@ -64,6 +64,12 @@ void canvas_api_free(CanvasApi* api) {
|
|||
free(api);
|
||||
}
|
||||
|
||||
void canvas_reset(CanvasApi* api) {
|
||||
assert(api);
|
||||
canvas_color_set(api, ColorBlack);
|
||||
canvas_font_set(api, FontSecondary);
|
||||
}
|
||||
|
||||
void canvas_commit(CanvasApi* api) {
|
||||
furi_assert(api);
|
||||
Canvas* canvas = (Canvas*)api;
|
||||
|
@ -144,23 +150,33 @@ void canvas_icon_draw(CanvasApi* api, uint8_t x, uint8_t y, Icon* icon) {
|
|||
void canvas_dot_draw(CanvasApi* api, uint8_t x, uint8_t y) {
|
||||
furi_assert(api);
|
||||
Canvas* canvas = (Canvas*)api;
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
u8g2_DrawPixel(&canvas->fb, x, y);
|
||||
}
|
||||
|
||||
void canvas_box_draw(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
|
||||
furi_assert(api);
|
||||
Canvas* canvas = (Canvas*)api;
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
u8g2_DrawBox(&canvas->fb, x, y, width, height);
|
||||
}
|
||||
|
||||
void canvas_draw_frame(CanvasApi* api, uint8_t x, uint8_t y, uint8_t width, uint8_t height) {
|
||||
furi_assert(api);
|
||||
Canvas* canvas = (Canvas*)api;
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
u8g2_DrawFrame(&canvas->fb, x, y, width, height);
|
||||
}
|
||||
|
||||
void canvas_draw_line(CanvasApi* api, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
|
||||
furi_assert(api);
|
||||
Canvas* canvas = (Canvas*)api;
|
||||
x1 += canvas->offset_x;
|
||||
y1 += canvas->offset_y;
|
||||
x2 += canvas->offset_x;
|
||||
y2 += canvas->offset_y;
|
||||
u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ CanvasApi* canvas_api_init();
|
|||
|
||||
void canvas_api_free(CanvasApi* api);
|
||||
|
||||
void canvas_reset(CanvasApi* api);
|
||||
|
||||
void canvas_commit(CanvasApi* api);
|
||||
|
||||
void canvas_frame_set(
|
||||
|
|
|
@ -41,7 +41,7 @@ void gui_update(Gui* gui) {
|
|||
}
|
||||
|
||||
bool gui_redraw_fs(Gui* gui) {
|
||||
canvas_frame_set(gui->canvas_api, 0, 0, 128, 64);
|
||||
canvas_frame_set(gui->canvas_api, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT);
|
||||
Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerFullscreen]);
|
||||
if(widget) {
|
||||
widget_draw(widget, gui->canvas_api);
|
||||
|
@ -51,18 +51,48 @@ bool gui_redraw_fs(Gui* gui) {
|
|||
}
|
||||
}
|
||||
|
||||
bool gui_redraw_status_bar(Gui* gui) {
|
||||
canvas_frame_set(gui->canvas_api, 0, 0, 128, 64);
|
||||
Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerStatusBar]);
|
||||
if(widget) {
|
||||
widget_draw(widget, gui->canvas_api);
|
||||
return true;
|
||||
void gui_redraw_status_bar(Gui* gui) {
|
||||
WidgetArray_it_t it;
|
||||
uint8_t x;
|
||||
uint8_t x_used = 0;
|
||||
uint8_t width;
|
||||
Widget* widget;
|
||||
// Right side
|
||||
x = 128;
|
||||
WidgetArray_it(it, gui->layers[GuiLayerStatusBarRight]);
|
||||
while(!WidgetArray_end_p(it) && x_used < GUI_STATUS_BAR_WIDTH) {
|
||||
// Render widget;
|
||||
widget = *WidgetArray_ref(it);
|
||||
if(widget_is_enabled(widget)) {
|
||||
width = widget_get_width(widget);
|
||||
if(!width) width = 8;
|
||||
x_used += width;
|
||||
x -= width;
|
||||
canvas_frame_set(gui->canvas_api, x, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT);
|
||||
widget_draw(widget, gui->canvas_api);
|
||||
}
|
||||
WidgetArray_next(it);
|
||||
}
|
||||
// Left side
|
||||
x = 0;
|
||||
WidgetArray_it(it, gui->layers[GuiLayerStatusBarLeft]);
|
||||
while(!WidgetArray_end_p(it) && x_used < GUI_STATUS_BAR_WIDTH) {
|
||||
// Render widget;
|
||||
widget = *WidgetArray_ref(it);
|
||||
if(widget_is_enabled(widget)) {
|
||||
width = widget_get_width(widget);
|
||||
if(!width) width = 8;
|
||||
x_used += width;
|
||||
canvas_frame_set(gui->canvas_api, x, GUI_STATUS_BAR_Y, width, GUI_STATUS_BAR_HEIGHT);
|
||||
widget_draw(widget, gui->canvas_api);
|
||||
x += width;
|
||||
}
|
||||
WidgetArray_next(it);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gui_redraw_normal(Gui* gui) {
|
||||
canvas_frame_set(gui->canvas_api, 0, 9, 128, 55);
|
||||
canvas_frame_set(gui->canvas_api, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT);
|
||||
Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerMain]);
|
||||
if(widget) {
|
||||
widget_draw(widget, gui->canvas_api);
|
||||
|
@ -72,7 +102,7 @@ bool gui_redraw_normal(Gui* gui) {
|
|||
}
|
||||
|
||||
bool gui_redraw_none(Gui* gui) {
|
||||
canvas_frame_set(gui->canvas_api, 0, 9, 118, 44);
|
||||
canvas_frame_set(gui->canvas_api, GUI_MAIN_X, GUI_MAIN_Y, GUI_MAIN_WIDTH, GUI_MAIN_HEIGHT);
|
||||
Widget* widget = gui_widget_find_enabled(gui->layers[GuiLayerNone]);
|
||||
if(widget) {
|
||||
widget_draw(widget, gui->canvas_api);
|
||||
|
@ -86,6 +116,8 @@ void gui_redraw(Gui* gui) {
|
|||
furi_assert(gui);
|
||||
gui_lock(gui);
|
||||
|
||||
canvas_reset(gui->canvas_api);
|
||||
|
||||
if(!gui_redraw_fs(gui)) {
|
||||
if(!gui_redraw_normal(gui)) {
|
||||
gui_redraw_none(gui);
|
||||
|
|
|
@ -3,10 +3,24 @@
|
|||
#include "widget.h"
|
||||
#include "canvas.h"
|
||||
|
||||
#define GUI_DISPLAY_WIDTH 128
|
||||
#define GUI_DISPLAY_HEIGHT 64
|
||||
|
||||
#define GUI_STATUS_BAR_X 0
|
||||
#define GUI_STATUS_BAR_Y 0
|
||||
#define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH
|
||||
#define GUI_STATUS_BAR_HEIGHT 8
|
||||
|
||||
#define GUI_MAIN_X 0
|
||||
#define GUI_MAIN_Y 9
|
||||
#define GUI_MAIN_WIDTH GUI_DISPLAY_WIDTH
|
||||
#define GUI_MAIN_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_MAIN_Y)
|
||||
|
||||
typedef enum {
|
||||
GuiLayerNone, /* Special layer for internal use only */
|
||||
|
||||
GuiLayerStatusBar, /* Status bar widget layer */
|
||||
GuiLayerStatusBarLeft, /* Status bar left-side widget layer, auto-layout */
|
||||
GuiLayerStatusBarRight, /* Status bar right-side widget layer, auto-layout */
|
||||
GuiLayerMain, /* Main widget layer, status bar is shown */
|
||||
GuiLayerFullscreen, /* Fullscreen widget layer */
|
||||
|
||||
|
|
|
@ -6,9 +6,20 @@
|
|||
|
||||
struct GuiEvent {
|
||||
PubSub* input_event_record;
|
||||
osTimerId_t timer;
|
||||
osMessageQueueId_t mqueue;
|
||||
};
|
||||
|
||||
void gui_event_timer_callback(void* arg) {
|
||||
assert(arg);
|
||||
GuiEvent* gui_event = arg;
|
||||
|
||||
GuiMessage message;
|
||||
message.type = GuiMessageTypeRedraw;
|
||||
|
||||
osMessageQueuePut(gui_event->mqueue, &message, 0, osWaitForever);
|
||||
}
|
||||
|
||||
void gui_event_input_events_callback(const void* value, void* ctx) {
|
||||
furi_assert(value);
|
||||
furi_assert(ctx);
|
||||
|
@ -29,6 +40,10 @@ GuiEvent* gui_event_alloc() {
|
|||
gui_event->mqueue = osMessageQueueNew(GUI_EVENT_MQUEUE_SIZE, sizeof(GuiMessage), NULL);
|
||||
furi_check(gui_event->mqueue);
|
||||
|
||||
gui_event->timer = osTimerNew(gui_event_timer_callback, osTimerPeriodic, gui_event, NULL);
|
||||
assert(gui_event->timer);
|
||||
osTimerStart(gui_event->timer, 1000 / 10);
|
||||
|
||||
// Input
|
||||
gui_event->input_event_record = furi_open("input_events");
|
||||
furi_check(gui_event->input_event_record != NULL);
|
||||
|
|
|
@ -10,15 +10,6 @@
|
|||
|
||||
// TODO add mutex to widget ops
|
||||
|
||||
struct Widget {
|
||||
Gui* gui;
|
||||
bool is_enabled;
|
||||
WidgetDrawCallback draw_callback;
|
||||
void* draw_callback_context;
|
||||
WidgetInputCallback input_callback;
|
||||
void* input_callback_context;
|
||||
};
|
||||
|
||||
Widget* widget_alloc(WidgetDrawCallback callback, void* callback_context) {
|
||||
Widget* widget = furi_alloc(sizeof(Widget));
|
||||
widget->is_enabled = true;
|
||||
|
@ -31,6 +22,26 @@ void widget_free(Widget* widget) {
|
|||
free(widget);
|
||||
}
|
||||
|
||||
void widget_set_width(Widget* widget, uint8_t width) {
|
||||
assert(widget);
|
||||
widget->width = width;
|
||||
}
|
||||
|
||||
uint8_t widget_get_width(Widget* widget) {
|
||||
assert(widget);
|
||||
return widget->width;
|
||||
}
|
||||
|
||||
void widget_set_height(Widget* widget, uint8_t height) {
|
||||
assert(widget);
|
||||
widget->height = height;
|
||||
}
|
||||
|
||||
uint8_t widget_get_height(Widget* widget) {
|
||||
assert(widget);
|
||||
return widget->height;
|
||||
}
|
||||
|
||||
void widget_enabled_set(Widget* widget, bool enabled) {
|
||||
furi_assert(widget);
|
||||
if(widget->is_enabled != enabled) {
|
||||
|
@ -80,7 +91,9 @@ void widget_draw(Widget* widget, CanvasApi* canvas_api) {
|
|||
void widget_input(Widget* widget, InputEvent* event) {
|
||||
furi_assert(widget);
|
||||
furi_assert(event);
|
||||
|
||||
furi_check(widget->gui);
|
||||
if(widget->input_callback) widget->input_callback(event, widget->input_callback_context);
|
||||
|
||||
if(widget->input_callback) {
|
||||
widget->input_callback(event, widget->input_callback_context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,51 @@ typedef struct Widget Widget;
|
|||
typedef void (*WidgetDrawCallback)(CanvasApi* api, void* context);
|
||||
typedef void (*WidgetInputCallback)(InputEvent* event, void* context);
|
||||
|
||||
/*
|
||||
* Widget allocator
|
||||
* always returns widget or stops system if not enough memory.
|
||||
*/
|
||||
Widget* widget_alloc();
|
||||
|
||||
/*
|
||||
* Widget deallocator
|
||||
* Ensure that widget was unregistered in GUI system before use.
|
||||
*/
|
||||
void widget_free(Widget* widget);
|
||||
|
||||
/*
|
||||
* Set widget width.
|
||||
* Will be used to limit canvas drawing area and autolayout feature.
|
||||
* @param width - wanted width, 0 - auto.
|
||||
*/
|
||||
void widget_set_width(Widget* widget, uint8_t width);
|
||||
uint8_t widget_get_width(Widget* widget);
|
||||
|
||||
/*
|
||||
* Set widget height.
|
||||
* Will be used to limit canvas drawing area and autolayout feature.
|
||||
* @param height - wanted height, 0 - auto.
|
||||
*/
|
||||
void widget_set_height(Widget* widget, uint8_t height);
|
||||
uint8_t widget_get_height(Widget* widget);
|
||||
|
||||
/*
|
||||
* Enable or disable widget rendering.
|
||||
* @param enabled.
|
||||
*/
|
||||
void widget_enabled_set(Widget* widget, bool enabled);
|
||||
bool widget_is_enabled(Widget* widget);
|
||||
|
||||
/*
|
||||
* Widget event callbacks
|
||||
* @param callback - appropriate callback function
|
||||
* @param context - context to pass to callback
|
||||
*/
|
||||
void widget_draw_callback_set(Widget* widget, WidgetDrawCallback callback, void* context);
|
||||
void widget_input_callback_set(Widget* widget, WidgetInputCallback callback, void* context);
|
||||
|
||||
// emit update signal
|
||||
/*
|
||||
* Emit update signal to GUI system.
|
||||
* Rendering will happen later after GUI system process signal.
|
||||
*/
|
||||
void widget_update(Widget* widget);
|
||||
|
|
|
@ -2,8 +2,31 @@
|
|||
|
||||
#include "gui_i.h"
|
||||
|
||||
struct Widget {
|
||||
Gui* gui;
|
||||
bool is_enabled;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
WidgetDrawCallback draw_callback;
|
||||
void* draw_callback_context;
|
||||
WidgetInputCallback input_callback;
|
||||
void* input_callback_context;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set GUI referenec.
|
||||
* @param gui - gui instance pointer.
|
||||
*/
|
||||
void widget_gui_set(Widget* widget, Gui* gui);
|
||||
|
||||
/*
|
||||
* Process draw call. Calls draw callback.
|
||||
* @param canvas_api - canvas to draw at.
|
||||
*/
|
||||
void widget_draw(Widget* widget, CanvasApi* canvas_api);
|
||||
|
||||
/*
|
||||
* Process input. Calls input callback.
|
||||
* @param event - pointer to input event.
|
||||
*/
|
||||
void widget_input(Widget* widget, InputEvent* event);
|
||||
|
|
|
@ -21,8 +21,9 @@ void irukagotchi_draw_callback(CanvasApi* canvas, void* context) {
|
|||
canvas->clear(canvas);
|
||||
canvas->set_color(canvas, ColorBlack);
|
||||
canvas->set_font(canvas, FontPrimary);
|
||||
canvas->draw_icon(canvas, 10, 20, irukagotchi->icon);
|
||||
canvas->draw_str(canvas, 30, 32, "Irukagotchi");
|
||||
canvas->draw_icon(canvas, 0, 0, irukagotchi->icon);
|
||||
canvas->draw_str(canvas, 80, 30, "111001");
|
||||
canvas->draw_str(canvas, 80, 42, "011010");
|
||||
}
|
||||
|
||||
void irukagotchi_input_callback(InputEvent* event, void* context) {
|
||||
|
@ -37,7 +38,7 @@ void irukagotchi_input_callback(InputEvent* event, void* context) {
|
|||
Irukagotchi* irukagotchi_alloc() {
|
||||
Irukagotchi* irukagotchi = furi_alloc(sizeof(Irukagotchi));
|
||||
|
||||
irukagotchi->icon = assets_icons_get(A_Tamagotchi_14);
|
||||
irukagotchi->icon = assets_icons_get(I_Flipper_young_80x60);
|
||||
icon_start_animation(irukagotchi->icon);
|
||||
|
||||
irukagotchi->widget = widget_alloc();
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
|
||||
#include "menu_event.h"
|
||||
#include "menu_item.h"
|
||||
#include <assets_icons.h>
|
||||
|
||||
struct Menu {
|
||||
MenuEvent* event;
|
||||
|
||||
// GUI
|
||||
Widget* widget;
|
||||
Icon* icon;
|
||||
|
||||
// State
|
||||
MenuItem* root;
|
||||
|
@ -56,7 +58,8 @@ void menu_build_main(Menu* menu) {
|
|||
// Root point
|
||||
menu->root = menu_item_alloc_menu(NULL, NULL);
|
||||
|
||||
menu->settings = menu_item_alloc_menu("Setting", NULL);
|
||||
Icon* icon = assets_icons_get(A_Bluetooth_14);
|
||||
menu->settings = menu_item_alloc_menu("Setting", icon);
|
||||
|
||||
menu_item_add(menu, menu->settings);
|
||||
}
|
||||
|
@ -103,16 +106,18 @@ void menu_widget_callback(CanvasApi* canvas, void* context) {
|
|||
canvas->set_font(canvas, FontPrimary);
|
||||
shift_position = (1 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas->draw_icon(canvas, 4, 24, menu_item_get_icon(item));
|
||||
canvas->draw_str(canvas, 22, 35, menu_item_get_label(item));
|
||||
canvas->draw_icon(canvas, 4, 25, menu_item_get_icon(item));
|
||||
canvas->draw_str(canvas, 22, 36, menu_item_get_label(item));
|
||||
// Third line
|
||||
canvas->set_font(canvas, FontSecondary);
|
||||
shift_position = (2 + position + items_count - 1) % (MenuItemArray_size(*items));
|
||||
item = *MenuItemArray_get(*items, shift_position);
|
||||
canvas->draw_icon(canvas, 4, 46, menu_item_get_icon(item));
|
||||
canvas->draw_str(canvas, 22, 57, menu_item_get_label(item));
|
||||
canvas->draw_icon(canvas, 4, 47, menu_item_get_icon(item));
|
||||
canvas->draw_str(canvas, 22, 58, menu_item_get_label(item));
|
||||
// Frame and scrollbar
|
||||
elements_frame(canvas, 0, 20, 128 - 5, 22);
|
||||
// elements_frame(canvas, 0, 0, 128 - 5, 21);
|
||||
elements_frame(canvas, 0, 21, 128 - 5, 21);
|
||||
// elements_frame(canvas, 0, 42, 128 - 5, 21);
|
||||
elements_scrollbar(canvas, position, items_count);
|
||||
} else {
|
||||
canvas->draw_str(canvas, 2, 32, "Empty");
|
||||
|
@ -122,9 +127,33 @@ void menu_widget_callback(CanvasApi* canvas, void* context) {
|
|||
release_mutex((ValueMutex*)context, menu);
|
||||
}
|
||||
|
||||
void menu_set_icon(Menu* menu, Icon* icon) {
|
||||
assert(menu);
|
||||
|
||||
if(menu->icon) {
|
||||
icon_stop_animation(menu->icon);
|
||||
}
|
||||
|
||||
menu->icon = icon;
|
||||
|
||||
if(menu->icon) {
|
||||
icon_start_animation(menu->icon);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_update(Menu* menu) {
|
||||
furi_assert(menu);
|
||||
|
||||
if(menu->current) {
|
||||
size_t position = menu_item_get_position(menu->current);
|
||||
MenuItemArray_t* items = menu_item_get_subitems(menu->current);
|
||||
size_t items_count = MenuItemArray_size(*items);
|
||||
if(items_count) {
|
||||
MenuItem* item = *MenuItemArray_get(*items, position);
|
||||
menu_set_icon(menu, menu_item_get_icon(item));
|
||||
}
|
||||
}
|
||||
|
||||
menu_event_activity_notify(menu->event);
|
||||
widget_update(menu->widget);
|
||||
}
|
||||
|
|
117
applications/power/power.c
Normal file
117
applications/power/power.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "power.h"
|
||||
|
||||
#include <flipper_v2.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/widget.h>
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define BATTERY_MIN_VOLTAGE 3.2f
|
||||
#define BATTERY_MAX_VOLTAGE 4.0f
|
||||
#define BATTERY_INIT 0xFFAACCEE
|
||||
|
||||
extern ADC_HandleTypeDef hadc1;
|
||||
|
||||
struct Power {
|
||||
Icon* usb_icon;
|
||||
Widget* usb_widget;
|
||||
|
||||
Icon* battery_icon;
|
||||
Widget* battery_widget;
|
||||
|
||||
uint32_t charge;
|
||||
};
|
||||
|
||||
void power_draw_usb_callback(CanvasApi* canvas, void* context) {
|
||||
assert(context);
|
||||
Power* power = context;
|
||||
canvas->draw_icon(canvas, 0, 0, power->usb_icon);
|
||||
}
|
||||
|
||||
void power_draw_battery_callback(CanvasApi* canvas, void* context) {
|
||||
assert(context);
|
||||
Power* power = context;
|
||||
|
||||
canvas->draw_icon(canvas, 0, 0, power->battery_icon);
|
||||
|
||||
if(power->charge != BATTERY_INIT) {
|
||||
float charge = ((float)power->charge / 1000 * 2 - BATTERY_MIN_VOLTAGE) /
|
||||
(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE);
|
||||
if(charge > 1) {
|
||||
charge = 1;
|
||||
}
|
||||
canvas->draw_box(canvas, 2, 2, charge * 14, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void power_input_events_callback(const void* value, void* ctx) {
|
||||
assert(ctx);
|
||||
Power* power = ctx;
|
||||
InputEvent* event = value;
|
||||
|
||||
if(event->input != InputCharging) return;
|
||||
|
||||
widget_enabled_set(power->usb_widget, event->state);
|
||||
widget_update(power->usb_widget);
|
||||
}
|
||||
|
||||
Power* power_alloc() {
|
||||
Power* power = furi_alloc(sizeof(Power));
|
||||
|
||||
power->usb_icon = assets_icons_get(I_USBConnected_15x8);
|
||||
power->usb_widget = widget_alloc();
|
||||
widget_set_width(power->usb_widget, icon_get_width(power->usb_icon));
|
||||
|
||||
ValueManager* input_state_manager = furi_open("input_state");
|
||||
InputState input_state;
|
||||
read_mutex_block(input_state_manager, &input_state, sizeof(input_state));
|
||||
widget_enabled_set(power->usb_widget, input_state.charging);
|
||||
|
||||
widget_draw_callback_set(power->usb_widget, power_draw_usb_callback, power);
|
||||
|
||||
power->battery_icon = assets_icons_get(I_Battery_19x8);
|
||||
power->battery_widget = widget_alloc();
|
||||
widget_set_width(power->battery_widget, icon_get_width(power->battery_icon));
|
||||
widget_draw_callback_set(power->battery_widget, power_draw_battery_callback, power);
|
||||
|
||||
PubSub* input_event_record = furi_open("input_events");
|
||||
assert(input_event_record);
|
||||
subscribe_pubsub(input_event_record, power_input_events_callback, power);
|
||||
|
||||
power->charge = BATTERY_INIT;
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
void power_free(Power* power) {
|
||||
assert(power);
|
||||
free(power);
|
||||
}
|
||||
|
||||
void power_task(void* p) {
|
||||
(void)p;
|
||||
Power* power = power_alloc();
|
||||
|
||||
FuriRecordSubscriber* gui_record = furi_open_deprecated("gui", false, false, NULL, NULL, NULL);
|
||||
assert(gui_record);
|
||||
GuiApi* gui = furi_take(gui_record);
|
||||
assert(gui);
|
||||
gui->add_widget(gui, power->usb_widget, GuiLayerStatusBarLeft);
|
||||
gui->add_widget(gui, power->battery_widget, GuiLayerStatusBarRight);
|
||||
furi_commit(gui_record);
|
||||
|
||||
if(!furi_create("power", power)) {
|
||||
printf("[power_task] unable to create power record\n");
|
||||
furiac_exit(NULL);
|
||||
}
|
||||
|
||||
furiac_ready();
|
||||
|
||||
while(1) {
|
||||
HAL_ADC_Start(&hadc1);
|
||||
if(HAL_ADC_PollForConversion(&hadc1, 1000) != HAL_TIMEOUT) {
|
||||
power->charge = HAL_ADC_GetValue(&hadc1);
|
||||
widget_update(power->battery_widget);
|
||||
}
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
3
applications/power/power.h
Normal file
3
applications/power/power.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
typedef struct Power Power;
|
3
assets/icons/IrukaGotchi/Flipper_idle_76x52.png
Normal file
3
assets/icons/IrukaGotchi/Flipper_idle_76x52.png
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:526c2637560aab102f259c029eeee93cc13e5cd8bdb935acb624f1318502f286
|
||||
size 652
|
3
assets/icons/IrukaGotchi/Flipper_young_80x60.png
Normal file
3
assets/icons/IrukaGotchi/Flipper_young_80x60.png
Normal file
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c7d2c5da9b957635189c2ee475d7065ae714fd6fb5d6f0c83ca5fa0bcd1497f8
|
||||
size 2653
|
Loading…
Reference in a new issue