added data partitions and app loading logic

This commit is contained in:
Ken Van Hoeylandt
2024-01-02 16:55:17 +01:00
parent b9427d4eba
commit c0824af966
26 changed files with 425 additions and 358 deletions
@@ -1,10 +1,9 @@
#include "desktop.h"
#include "furi_extra_defines.h"
static int32_t prv_desktop_main(void* param) {
static void desktop_start(void* param) {
UNUSED(param);
printf("desktop app init\n");
return 0;
}
const AppManifest desktop_app = {
@@ -12,6 +11,8 @@ const AppManifest desktop_app = {
.name = "Desktop",
.icon = NULL,
.type = AppTypeService,
.entry_point = &prv_desktop_main,
.on_start = &desktop_start,
.on_stop = NULL,
.on_show = NULL,
.stack_size = AppStackSizeNormal
};
+68 -25
View File
@@ -2,13 +2,15 @@
#include "esp_lvgl_port.h"
#include "furi_extra_defines.h"
#include "gui_i.h"
#include "log.h"
#include "record.h"
#define TAG "gui"
// Forward declarations from gui_draw.c
bool gui_redraw_fs(Gui* gui);
void gui_redraw(Gui* gui);
bool gui_redraw_fs(Gui*);
void gui_redraw(Gui*);
static int32_t gui_main(void*);
ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
// Iterating backward
@@ -17,6 +19,7 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) {
while (!ViewPortArray_end_p(it)) {
ViewPort* view_port = *ViewPortArray_ref(it);
if (view_port_is_enabled(view_port)) {
ViewPort* view_port = *ViewPortArray_ref(it);
return view_port;
}
ViewPortArray_previous(it);
@@ -45,9 +48,10 @@ size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) {
}
void gui_update(Gui* gui) {
ESP_LOGI(TAG, "gui_update");
furi_assert(gui);
furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW);
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
}
void gui_lock(Gui* gui) {
@@ -170,28 +174,20 @@ void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) {
gui_update(gui);
}
void gui_set_lockdown(Gui* gui, bool lockdown) {
furi_assert(gui);
gui_lock(gui);
gui->lockdown = lockdown;
gui_unlock(gui);
// Request redraw
gui_update(gui);
}
Gui* gui_alloc() {
Gui* gui = malloc(sizeof(Gui));
gui->thread_id = furi_thread_get_current_id();
gui->thread = furi_thread_alloc_ex(
"gui",
2048,
&gui_main,
NULL
);
gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
furi_check(lvgl_port_lock(100));
gui->lvgl_parent = lv_scr_act();
lvgl_port_unlock();
gui->lockdown = false;
furi_check(gui->mutex);
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_init(gui->layers[i]);
}
@@ -207,11 +203,22 @@ Gui* gui_alloc() {
return gui;
}
__attribute((__noreturn__)) int32_t prv_gui_main(void* parameter) {
UNUSED(parameter);
Gui* gui = gui_alloc();
void gui_free(Gui* gui) {
furi_thread_free(gui->thread);
furi_record_create(RECORD_GUI, gui);
if (gui->mutex) {
furi_mutex_free(gui->mutex);
}
for (size_t i = 0; i < GuiLayerMAX; i++) {
ViewPortArray_clear(gui->layers[i]);
}
free(gui);
}
static int32_t gui_main(void* parameter) {
UNUSED(parameter);
while (1) {
uint32_t flags = furi_thread_flags_wait(
@@ -229,11 +236,45 @@ __attribute((__noreturn__)) int32_t prv_gui_main(void* parameter) {
}*/
// Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) {
// Clear flags that arrived on input step
FURI_LOG_D(TAG, "redraw requested");
furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW);
gui_redraw(gui);
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_redraw(gui);
})
}
if (flags & GUI_THREAD_FLAG_EXIT) {
furi_thread_flags_clear(GUI_THREAD_FLAG_EXIT);
break;
}
}
return 0;
}
static void gui_start(void* parameter) {
UNUSED(parameter);
Gui* gui = gui_alloc();
furi_record_create(RECORD_GUI, gui);
furi_thread_set_priority(gui->thread, FuriThreadPriorityHigh);
furi_thread_start(gui->thread);
}
static void gui_stop() {
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_lock(gui);
FuriThreadId thread_id = furi_thread_get_id(gui->thread);
furi_thread_flags_set(thread_id, GUI_THREAD_FLAG_EXIT);
furi_thread_join(gui->thread);
gui_unlock(gui);
gui_free(gui);
})
furi_record_destroy(RECORD_GUI);
}
const AppManifest gui_app = {
@@ -241,6 +282,8 @@ const AppManifest gui_app = {
.name = "GUI",
.icon = NULL,
.type = AppTypeService,
.entry_point = &prv_gui_main,
.on_start = &gui_start,
.on_stop = &gui_stop,
.on_show = NULL,
.stack_size = AppStackSizeNormal
};
@@ -71,16 +71,6 @@ void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port);
*/
void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port);
/** Set lockdown mode
*
* When lockdown mode is enabled, only GuiLayerDesktop is shown.
* This feature prevents services from showing sensitive information when flipper is locked.
*
* @param gui Gui instance
* @param lockdown bool, true if enabled
*/
void gui_set_lockdown(Gui* gui, bool lockdown);
#ifdef __cplusplus
}
#endif
@@ -1,5 +1,6 @@
#include "check.h"
#include "gui_i.h"
#include "esp_lvgl_port.h"
static void gui_redraw_status_bar(Gui* gui, bool need_attention) {
/*
@@ -188,24 +189,16 @@ void gui_redraw(Gui* gui) {
furi_assert(gui);
gui_lock(gui);
furi_check(lvgl_port_lock(100));
lv_obj_clean(gui->lvgl_parent);
lvgl_port_unlock();
if (gui->lockdown) {
ESP_LOGI("gui", "gui_redraw with lockdown");
gui_redraw_desktop(gui);
bool need_attention =
(gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 ||
gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0);
gui_redraw_status_bar(gui, need_attention);
} else {
gui_redraw_desktop(gui);
ESP_LOGI("gui", "gui_redraw");
if (!gui_redraw_fs(gui)) {
if (!gui_redraw_window(gui)) {
gui_redraw_desktop(gui);
}
gui_redraw_status_bar(gui, false);
gui_redraw_desktop(gui);
if (!gui_redraw_fs(gui)) {
if (!gui_redraw_window(gui)) {
gui_redraw_desktop(gui);
}
gui_redraw_status_bar(gui, false);
}
gui_unlock(gui);
@@ -34,7 +34,8 @@
#define GUI_THREAD_FLAG_DRAW (1 << 0)
#define GUI_THREAD_FLAG_INPUT (1 << 1)
#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT)
#define GUI_THREAD_FLAG_EXIT (1 << 2)
#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT | GUI_THREAD_FLAG_EXIT)
ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST);
@@ -46,11 +47,10 @@ typedef struct {
/** Gui structure */
struct Gui {
// Thread and lock
FuriThreadId thread_id;
FuriThread* thread;
FuriMutex* mutex;
// Layers and Canvas
bool lockdown;
ViewPortArray_t layers[GuiLayerMAX];
lv_obj_t* lvgl_parent;
@@ -89,11 +89,10 @@ void view_port_draw(ViewPort* view_port, lv_obj_t* parent) {
furi_check(view_port->gui);
if (view_port->draw_callback) {
furi_check(lvgl_port_lock(100));
furi_check(lvgl_port_lock(100)); // TODO: fail safely
lv_obj_clean(parent);
lvgl_port_unlock();
view_port->draw_callback(parent, view_port->draw_callback_context);
lvgl_port_unlock();
}
furi_mutex_release(view_port->mutex);
@@ -5,11 +5,19 @@
#include "loader_i.h"
#include <sys/cdefs.h>
#include "esp_heap_caps.h"
#include "apps/services/gui/gui.h"
#define TAG "Loader"
#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF
LoaderStatus loader_start(Loader* loader, const char* id, const char* args, FuriString* error_message) {
static int32_t loader_main(void* p);
static LoaderStatus loader_do_start_by_id(
Loader* loader,
const char* id,
const char* args,
FuriString* error_message
);
LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args, FuriString* error_message) {
LoaderMessage message;
LoaderMessageLoaderStatusResult result;
@@ -24,32 +32,23 @@ LoaderStatus loader_start(Loader* loader, const char* id, const char* args, Furi
return result.value;
}
bool loader_lock(Loader* loader) {
void loader_start_app_nonblocking(Loader* loader, const char* id, const char* args) {
LoaderMessage message;
LoaderMessageBoolResult result;
message.type = LoaderMessageTypeLock;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
LoaderMessageLoaderStatusResult result;
void loader_unlock(Loader* loader) {
LoaderMessage message;
message.type = LoaderMessageTypeUnlock;
message.type = LoaderMessageTypeStartByName;
message.start.id = id;
message.start.args = args;
message.start.error_message = NULL;
message.api_lock = NULL;
message.status_value = &result;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
bool loader_is_locked(Loader* loader) {
void loader_stop_app(Loader* loader) {
LoaderMessage message;
LoaderMessageBoolResult result;
message.type = LoaderMessageTypeIsLocked;
message.api_lock = api_lock_alloc_locked();
message.bool_value = &result;
message.type = LoaderMessageTypeAppStop;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return result.value;
}
FuriPubSub* loader_get_pubsub(Loader* loader) {
@@ -60,43 +59,28 @@ FuriPubSub* loader_get_pubsub(Loader* loader) {
return loader->pubsub;
}
// callbacks
static void loader_thread_state_callback(FuriThreadState thread_state, void* context) {
furi_assert(context);
Loader* loader = context;
if (thread_state == FuriThreadStateRunning) {
LoaderEvent event;
event.type = LoaderEventTypeApplicationStarted;
furi_pubsub_publish(loader->pubsub, &event);
} else if (thread_state == FuriThreadStateStopped) {
LoaderMessage message;
message.type = LoaderMessageTypeAppClosed;
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
}
}
// implementation
static Loader* loader_alloc() {
Loader* loader = malloc(sizeof(Loader));
loader->pubsub = furi_pubsub_alloc();
loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage));
loader->thread = furi_thread_alloc_ex(
"loader",
2048,
&loader_main,
NULL
);
loader->app_data.args = NULL;
loader->app_data.thread = NULL;
loader->app_data.app = NULL;
return loader;
}
static void loader_start_app_thread(Loader* loader) {
// setup thread state callbacks
furi_thread_set_state_context(loader->app_data.thread, loader);
furi_thread_set_state_callback(loader->app_data.thread, loader_thread_state_callback);
// start app thread
furi_thread_start(loader->app_data.thread);
static void loader_free(Loader* loader) {
furi_pubsub_free(loader->pubsub);
furi_message_queue_free(loader->queue);
furi_thread_free(loader->thread);
free(loader);
}
static void loader_log_status_error(
@@ -115,7 +99,7 @@ static void loader_log_status_error(
static LoaderStatus loader_make_status_error(
LoaderStatus status,
FuriString* error_message,
FuriString* _Nullable error_message,
const char* format,
...
) {
@@ -134,46 +118,41 @@ static LoaderStatus loader_make_success_status(FuriString* error_message) {
return LoaderStatusOk;
}
static void loader_start_app(
static void loader_start_app_with_manifest(
Loader* loader,
const AppManifest* _Nonnull manifest,
const char* args
) {
FURI_LOG_I(TAG, "Starting %s", manifest->id);
App* _Nonnull app = furi_app_alloc(manifest);
loader->app_data.app = app;
loader->app_data.args = (void*)args;
FuriThread* thread = furi_app_alloc_thread(loader->app_data.app, args);
loader->app_data.app->thread = thread;
loader->app_data.thread = thread;
if (manifest->on_start != NULL) {
manifest->on_start((void*)args);
}
loader_start_app_thread(loader);
if (manifest->on_show != NULL) {
ViewPort* view_port = view_port_alloc();
loader->app_data.view_port = view_port;
view_port_draw_callback_set(view_port, manifest->on_show, NULL);
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_add_view_port(gui, view_port, GuiLayerFullscreen); // TODO: layer type
})
} else {
loader->app_data.view_port = NULL;
}
}
// process messages
static bool loader_do_is_locked(Loader* loader) {
return loader->app_data.thread != NULL;
}
static LoaderStatus loader_do_start_by_id(
Loader* loader,
const char* id,
const char* args,
FuriString* error_message
FuriString* _Nullable error_message
) {
// check lock
if (loader_do_is_locked(loader)) {
const char* current_thread_name =
furi_thread_get_name(furi_thread_get_id(loader->app_data.thread));
return loader_make_status_error(
LoaderStatusErrorAppStarted,
error_message,
"Loader is locked, please close the \"%s\" first",
current_thread_name
);
}
FURI_LOG_I(TAG, "loader start by id %s", id);
const AppManifest* manifest = app_manifest_registry_find_by_id(id);
if (manifest == NULL) {
@@ -185,54 +164,44 @@ static LoaderStatus loader_do_start_by_id(
);
}
loader_start_app(loader, manifest, args);
loader_start_app_with_manifest(loader, manifest, args);
return loader_make_success_status(error_message);
}
static bool loader_do_lock(Loader* loader) {
if (loader->app_data.thread) {
return false;
static void loader_do_stop_app(Loader* loader) {
App* app = loader->app_data.app;
if (app == NULL) {
FURI_LOG_W(TAG, "Stop app: no app running");
return;
}
loader->app_data.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE;
return true;
}
FURI_LOG_I(TAG, "Stopping %s", app->manifest->id);
static void loader_do_unlock(Loader* loader) {
furi_check(loader->app_data.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE);
loader->app_data.thread = NULL;
}
ViewPort* view_port = loader->app_data.view_port;
if (view_port) {
FURI_RECORD_TRANSACTION(RECORD_GUI, Gui*, gui, {
gui_remove_view_port(gui, view_port);
})
view_port_free(view_port);
loader->app_data.view_port = NULL;
}
static void loader_do_app_closed(Loader* loader) {
furi_assert(loader->app_data.thread);
furi_thread_join(loader->app_data.thread);
FURI_LOG_I(
TAG,
"App %s returned: %li",
loader->app_data.app->manifest->id,
furi_thread_get_return_code(loader->app_data.thread)
);
if (app->manifest->on_stop) {
app->manifest->on_stop();
}
if (loader->app_data.args) {
free(loader->app_data.args);
loader->app_data.args = NULL;
}
if (loader->app_data.app) {
furi_app_free(loader->app_data.app);
loader->app_data.app = NULL;
} else {
assert(loader->app_data.thread == NULL);
furi_thread_free(loader->app_data.thread);
}
loader->app_data.thread = NULL;
furi_app_free(loader->app_data.app);
loader->app_data.app = NULL;
FURI_LOG_I(
TAG,
"Application stopped. Free heap: %zu",
heap_caps_get_free_size(MALLOC_CAP_DEFAULT)
heap_caps_get_free_size(MALLOC_CAP_INTERNAL)
);
LoaderEvent event;
@@ -242,42 +211,76 @@ static void loader_do_app_closed(Loader* loader) {
// app
_Noreturn int32_t loader_main(void* p) {
static int32_t loader_main(void* p) {
UNUSED(p);
Loader* loader = loader_alloc();
furi_record_create(RECORD_LOADER, loader);
FuriMessageQueue* queue = NULL;
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
queue = loader->queue;
})
furi_check(queue != NULL);
LoaderMessage message;
while (true) {
if (furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) {
bool exit_requested = false;
while (!exit_requested) {
if (furi_message_queue_get(queue, &message, FuriWaitForever) == FuriStatusOk) {
FURI_LOG_I(TAG, "processing message of type %d", message.type);
switch (message.type) {
case LoaderMessageTypeStartByName:
message.status_value->value = loader_do_start_by_id(
loader,
message.start.id,
message.start.args,
message.start.error_message
);
api_lock_unlock(message.api_lock);
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
if (loader->app_data.app) {
loader_do_stop_app(loader);
}
message.status_value->value = loader_do_start_by_id(
loader,
message.start.id,
message.start.args,
message.start.error_message
);
if (message.api_lock) {
api_lock_unlock(message.api_lock);
}
})
break;
case LoaderMessageTypeIsLocked:
message.bool_value->value = loader_do_is_locked(loader);
api_lock_unlock(message.api_lock);
case LoaderMessageTypeAppStop:
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
loader_do_stop_app(loader);
})
break;
case LoaderMessageTypeAppClosed:
loader_do_app_closed(loader);
break;
case LoaderMessageTypeLock:
message.bool_value->value = loader_do_lock(loader);
api_lock_unlock(message.api_lock);
break;
case LoaderMessageTypeUnlock:
loader_do_unlock(loader);
case LoaderMessageTypeExit:
exit_requested = true;
break;
}
}
}
return 0;
}
static void loader_start(void* p) {
Loader* loader = loader_alloc();
furi_record_create(RECORD_LOADER, loader);
furi_thread_set_priority(loader->thread, FuriThreadPriorityHigh);
furi_thread_start(loader->thread);
}
static void loader_stop() {
LoaderMessage message = {
.api_lock = NULL,
.type = LoaderMessageTypeExit
};
FURI_RECORD_TRANSACTION(RECORD_LOADER, Loader*, loader, {
furi_message_queue_put(loader->queue, &message, FuriWaitForever);
furi_thread_join(loader->thread);
furi_thread_free(loader->thread);
loader->thread = NULL;
loader_free(loader);
})
furi_record_destroy(RECORD_LOADER);
}
const AppManifest loader_app = {
@@ -285,6 +288,8 @@ const AppManifest loader_app = {
.name = "Loader",
.icon = NULL,
.type = AppTypeService,
.entry_point = &loader_main,
.on_start = &loader_start,
.on_stop = &loader_stop,
.on_show = NULL,
.stack_size = AppStackSizeNormal
};
@@ -28,14 +28,24 @@ typedef struct {
} LoaderEvent;
/**
* @brief Start application
* @param[in] instance loader instance
* @brief Close any running app, then start new one. Blocking.
* @param[in] loader loader instance
* @param[in] id application name or id
* @param[in] args application arguments
* @param[out] error_message detailed error message, can be NULL
* @return LoaderStatus
*/
LoaderStatus loader_start(Loader* instance, const char* id, const char* args, FuriString* error_message);
LoaderStatus loader_start_app(Loader* loader, const char* id, const char* args, FuriString* error_message);
/**
* @brief Close any running app, then start new one. Non-blocking.
* @param[in] loader loader instance
* @param[in] id application name or id
* @param[in] args application arguments
*/
void loader_start_app_nonblocking(Loader* loader, const char* id, const char* args);
void loader_stop_app(Loader* loader);
/**
* @brief Start application with GUI error message
@@ -46,26 +56,6 @@ LoaderStatus loader_start(Loader* instance, const char* id, const char* args, Fu
*/
//LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args);
/**
* @brief Lock application start
* @param[in] instance loader instance
* @return true on success
*/
bool loader_lock(Loader* instance);
/**
* @brief Unlock application start
* @param[in] instance loader instance
*/
void loader_unlock(Loader* instance);
/**
* @brief Check if loader is locked
* @param[in] instance loader instance
* @return true if locked
*/
bool loader_is_locked(Loader* instance);
/**
* @brief Show loader menu
* @param[in] instance loader instance
@@ -5,14 +5,16 @@
#include "message_queue.h"
#include "pubsub.h"
#include "thread.h"
#include "apps/services/gui/view_port.h"
typedef struct {
char* args;
FuriThread* thread;
App* app;
ViewPort* view_port;
} LoaderAppData;
struct Loader {
FuriThread* thread;
FuriPubSub* pubsub;
FuriMessageQueue* queue;
LoaderAppData app_data;
@@ -20,10 +22,8 @@ struct Loader {
typedef enum {
LoaderMessageTypeStartByName,
LoaderMessageTypeAppClosed,
LoaderMessageTypeLock,
LoaderMessageTypeUnlock,
LoaderMessageTypeIsLocked,
LoaderMessageTypeAppStop,
LoaderMessageTypeExit,
} LoaderMessageType;
typedef struct {
@@ -41,6 +41,8 @@ typedef struct {
} LoaderMessageBoolResult;
typedef struct {
// This lock blocks anyone from starting an app as long
// as an app is already running via loader_start()
FuriApiLock api_lock;
LoaderMessageType type;
@@ -1,17 +1,50 @@
#include "system_info.h"
#include "furi_extra_defines.h"
#include "thread.h"
#include "lvgl.h"
static int32_t system_info_entry_point(void* param) {
static void system_info_main(void* param) {
UNUSED(param);
printf(
"Heap memory available: %d / %d\n",
heap_caps_get_free_size(MALLOC_CAP_DEFAULT),
heap_caps_get_total_size(MALLOC_CAP_DEFAULT)
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_total_size(MALLOC_CAP_INTERNAL)
);
return 0;
printf(
"SPI memory available: %d / %d\n",
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_total_size(MALLOC_CAP_SPIRAM)
);
}
static void app_show(lv_obj_t* parent, void* context) {
UNUSED(context);
lv_obj_t* heap_info = lv_label_create(parent);
lv_label_set_recolor(heap_info, true);
lv_obj_set_width(heap_info, 200);
lv_obj_set_style_text_align(heap_info, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text_fmt(
heap_info,
"Heap available:\n%d / %d",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_total_size(MALLOC_CAP_INTERNAL)
);
lv_obj_align(heap_info, LV_ALIGN_CENTER, 0, -20);
lv_obj_t* spi_info = lv_label_create(parent);
lv_label_set_recolor(spi_info, true);
lv_obj_set_width(spi_info, 200);
lv_obj_set_style_text_align(spi_info, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text_fmt(
spi_info,
"SPI available\n%d / %d",
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_total_size(MALLOC_CAP_SPIRAM)
);
lv_obj_align(spi_info, LV_ALIGN_CENTER, 0, 20);
}
AppManifest system_info_app = {
@@ -19,6 +52,8 @@ AppManifest system_info_app = {
.name = "System Info",
.icon = NULL,
.type = AppTypeSystem,
.entry_point = &system_info_entry_point,
.on_start = NULL,
.on_stop = NULL,
.on_show = app_show,
.stack_size = AppStackSizeNormal
};
+4 -9
View File
@@ -4,6 +4,7 @@
#include "devices_i.h"
#include "furi.h"
#include "graphics_i.h"
#include "partitions.h"
#define TAG "nanobake"
@@ -18,15 +19,7 @@ extern const AppManifest system_info_app;
void start_service(const AppManifest* _Nonnull manifest) {
// TODO: keep track of running services
FURI_LOG_I(TAG, "Starting service %s", manifest->name);
FuriThread* thread = furi_thread_alloc_ex(
manifest->name,
manifest->stack_size,
manifest->entry_point,
NULL
);
furi_thread_mark_as_service(thread);
furi_thread_set_appid(thread, manifest->id);
furi_thread_start(thread);
manifest->on_start(NULL);
}
static void register_apps(Config* _Nonnull config) {
@@ -51,6 +44,8 @@ static void start_services() {
__attribute__((unused)) extern void nanobake_start(Config* _Nonnull config) {
furi_init();
nb_partitions_init();
Devices hardware = nb_devices_create(config);
/*NbLvgl lvgl =*/nb_graphics_init(&hardware);
+66
View File
@@ -0,0 +1,66 @@
#include "partitions.h"
#include "esp_spiffs.h"
#include "log.h"
#include "nvs_flash.h"
static const char* TAG = "filesystem";
static esp_err_t nvs_flash_init_safely() {
esp_err_t result = nvs_flash_init();
if (result == ESP_ERR_NVS_NO_FREE_PAGES || result == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
result = nvs_flash_init();
}
return result;
}
static esp_err_t spiffs_init(esp_vfs_spiffs_conf_t* conf) {
esp_err_t ret = esp_vfs_spiffs_register(conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
FURI_LOG_E(TAG, "Failed to mount or format filesystem %s", conf->base_path);
} else if (ret == ESP_ERR_NOT_FOUND) {
FURI_LOG_E(TAG, "Failed to find SPIFFS partition %s", conf->base_path);
} else {
FURI_LOG_E(TAG, "Failed to initialize SPIFFS %s (%s)", conf->base_path, esp_err_to_name(ret));
}
return ESP_FAIL;
}
size_t total = -1, used = 0;
ret = esp_spiffs_info(NULL, &total, &used);
if (ret != ESP_OK) {
FURI_LOG_E(TAG, "Failed to get SPIFFS partition information for %s (%s)", conf->base_path, esp_err_to_name(ret));
} else {
FURI_LOG_I(TAG, "Partition size for %s: total: %d, used: %d", conf->base_path, total, used);
}
return ESP_OK;
}
esp_err_t nb_partitions_init() {
ESP_ERROR_CHECK(nvs_flash_init_safely());
esp_vfs_spiffs_conf_t assets_spiffs = {
.base_path = MOUNT_POINT_ASSETS,
.partition_label = NULL,
.max_files = 4,
.format_if_mount_failed = false
};
if (spiffs_init(&assets_spiffs) != ESP_OK) {
return ESP_FAIL;
}
esp_vfs_spiffs_conf_t config_spiffs = {
.base_path = MOUNT_POINT_CONFIG,
.partition_label = "config",
.max_files = 2,
.format_if_mount_failed = false
};
if (spiffs_init(&config_spiffs) != ESP_OK) {
return ESP_FAIL;
}
return ESP_OK;
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "esp_err.h"
#define MOUNT_POINT_ASSETS "/assets"
#define MOUNT_POINT_CONFIG "/config"
esp_err_t nb_partitions_init();