Boot splash and more (#98)

* Boot splash and more

- Added developer sdkconfig
- Refactored the way FreeRTOS includes are included
- Improved Gui/Loader logic
- Implemented boot app with splash screen

* Updated naming for Gui and Loader services

* Renamed Screenshot service methods

* Renames

* Service renames
This commit is contained in:
Ken Van Hoeylandt 2024-11-30 15:37:16 +01:00 committed by GitHub
parent 3f62ec2efa
commit 0188ce721c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 726 additions and 307 deletions

View File

@ -16,5 +16,16 @@ menu "Tactility App"
bool "M5Stack CoreS3"
config TT_BOARD_WAVESHARE_S3_TOUCH
bool "Waveshare S3 Touch LCD 4.3\""
help
Select a board/hardware configuration.
Use TT_BOARD_CUSTOM if you will manually configure the board in your project.
endchoice
config TT_SPLASH_DURATION
int "Splash Duration (ms)"
default 1500
range 0 3000
help
The minimum time to show the splash screen in milliseconds.
When set to 0, startup will continue to desktop as soon as boot operations are finished.
endmenu

View File

@ -9,9 +9,7 @@
#include <esp_lcd_panel_rgb.h>
#include <esp_timer.h>
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "RtosCompatSemaphore.h"
#define TAG "waveshare_s3_touch_display"

View File

@ -1,3 +1,13 @@
## Tactility
The Tactility logo copyrights are owned by Ken Van Hoeylandt.
Firmwares built from [the original repository](https://github.com/ByteWelder/Tactility) can be redistributed with the Tactility logo.
For other usages, [contact me](https://kenvanhoeylandt.net).
The Tactility firmware and code are published under [GPL License Version 3](./LICENSE.md).
# Dependencies
### ESP-IDF
This project uses ESP-IDF to compile the ESP32 firmware.
@ -15,13 +25,12 @@ Website: https://github.com/flipperdevices/flipperzero-firmware/
License: [GPL v3.0](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/LICENSE)
### Google Fonts
Website: https://fonts.google.com/icons
License: [Apache License, version 2.0](https://fonts.google.com/attribution)
### Components
### Other Components
See `/components` for the respective projects and their licenses.

BIN
Data/assets/boot_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="800"
height="800"
viewBox="0 0 211.66667 211.66667"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="Tactility.svg"
inkscape:export-filename="Tactility.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="1"
inkscape:cx="410.5"
inkscape:cy="336.5"
inkscape:window-width="2115"
inkscape:window-height="1295"
inkscape:window-x="26"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg1"
showgrid="false" />
<defs
id="defs1">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect4"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.9852864,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect3"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,9.96875,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect2"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,10.022406,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,10.022406,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect34"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.5761962,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect33"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.7667784,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect32"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,10.151862,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect31"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,9.8324877,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect30"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,10.269671,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect29"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,9.7906096,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<g
id="g4"
inkscape:label="Large with textt"
inkscape:export-filename="g4.png"
inkscape:export-xdpi="21.879999"
inkscape:export-ydpi="21.879999">
<g
id="g29"
transform="translate(50,-70)"
inkscape:label="Glyph">
<g
id="g28"
inkscape:label="Upright T">
<path
style="fill:#ffffff;stroke-width:0.289836;paint-order:stroke fill markers"
id="rect26"
width="60"
height="10"
x="10"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect1"
d="M 20.022406,150 H 70 v 10 H 10 a 10.011209,10.011209 135.06412 0 1 10.022406,-10 z" />
<path
style="fill:#ffffff;stroke-width:0.264583;paint-order:stroke fill markers"
id="rect28"
width="10"
height="60"
x="40"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect4"
d="m 40,150 h 10 v 60 h -0.01471 A 9.9852864,9.9852864 45 0 1 40,200.01471 Z" />
</g>
<g
id="g28-0"
transform="rotate(180,55,165)"
inkscape:label="Upside-down T">
<path
style="fill:#ffffff;stroke-width:0.289836;paint-order:stroke fill markers"
id="rect26-4"
width="60"
height="10"
x="10"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect3"
d="M 19.96875,150 H 70 v 10 H 10 v -0.0312 A 9.96875,9.96875 135 0 1 19.96875,150 Z" />
<path
style="fill:#ffffff;stroke-width:0.264583;paint-order:stroke fill markers"
id="rect28-8"
width="10"
height="60"
x="40"
y="150"
sodipodi:type="rect"
inkscape:path-effect="#path-effect2"
d="m 40,150 h 10 v 60 A 10.011209,10.011209 45.064117 0 1 40,199.97759 Z" />
</g>
</g>
<text
xml:space="preserve"
style="font-weight:bold;font-size:16.7003px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#ffffff;stroke-width:0.0490925;paint-order:stroke fill markers"
x="104.63994"
y="162.85924"
id="text29"
inkscape:label="Text"><tspan
sodipodi:role="line"
id="tspan29"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:16.7003px;font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro Bold';stroke-width:0.0490925"
x="104.63994"
y="162.85924">Tactility</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -72,6 +72,7 @@ public:
Flags getFlags() const;
void setFlags(Flags flags);
Flags& mutableFlags() { return flags; }
_Nullable void* getData() const;
void setData(void* data);

View File

@ -33,7 +33,7 @@ struct Gui {
};
/** Update GUI, request redraw */
void request_draw();
void requestDraw();
/** Lock GUI */
void lock();

View File

@ -7,19 +7,50 @@
#include "Thread.h"
#include "service/gui/ViewPort.h"
#include "service/loader/Loader.h"
#include "RtosCompatSemaphore.h"
#include <stack>
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#else
#include "FreeRTOS.h"
#include "semphr.h"
#endif
namespace tt::service::loader {
#define APP_STACK_SIZE 32
// region LoaderEvent
typedef enum {
LoaderEventTypeApplicationStarted,
LoaderEventTypeApplicationShowing,
LoaderEventTypeApplicationHiding,
LoaderEventTypeApplicationStopped
} LoaderEventType;
typedef struct {
app::AppInstance& app;
} LoaderEventAppStarted;
typedef struct {
app::AppInstance& app;
} LoaderEventAppShowing;
typedef struct {
app::AppInstance& app;
} LoaderEventAppHiding;
typedef struct {
const app::Manifest& manifest;
} LoaderEventAppStopped;
typedef struct {
LoaderEventType type;
union {
LoaderEventAppStarted app_started;
LoaderEventAppShowing app_showing;
LoaderEventAppHiding app_hiding;
LoaderEventAppStopped app_stopped;
};
} LoaderEvent;
// endregion LoaderEvent
// region LoaderMessage
typedef enum {
LoaderMessageTypeNone,
@ -104,11 +135,13 @@ public:
}
};
// endregion LoaderMessage
struct Loader {
Thread* thread;
PubSub* pubsub_internal;
PubSub* pubsub_external;
MessageQueue queue = MessageQueue(1, sizeof(LoaderMessage));
MessageQueue queue = MessageQueue(2, sizeof(LoaderMessage)); // 2 entries, so you can stop the current app while starting a new one without blocking
Mutex* mutex;
std::stack<app::AppInstance*> app_stack;
};

View File

@ -35,6 +35,7 @@ static const std::vector<const service::Manifest*> system_services = {
// region Default apps
namespace app {
namespace boot { extern const Manifest manifest; }
namespace desktop { extern const Manifest manifest; }
namespace files { extern const Manifest manifest; }
namespace gpio { extern const Manifest manifest; }
@ -57,6 +58,7 @@ extern const app::Manifest screenshot_app;
#endif
static const std::vector<const app::Manifest*> system_apps = {
&app::boot::manifest,
&app::desktop::manifest,
&app::display::manifest,
&app::files::manifest,
@ -145,11 +147,11 @@ void init(const Configuration* config) {
register_user_apps(config->apps);
TT_LOG_I(TAG, "init starting desktop app");
service::loader::start_app(app::desktop::manifest.id, true, Bundle());
service::loader::startApp(app::boot::manifest.id, true, Bundle());
if (config->auto_start_app_id) {
TT_LOG_I(TAG, "init auto-starting %s", config->auto_start_app_id);
service::loader::start_app(config->auto_start_app_id, true, Bundle());
service::loader::startApp(config->auto_start_app_id, true, Bundle());
}
TT_LOG_I(TAG, "init complete");

View File

@ -12,6 +12,8 @@ namespace tt::app {
class App;
typedef enum {
/** Boot screen, shown before desktop is launched. */
TypeBoot,
/** A desktop app sits at the root of the app stack managed by the Loader service */
TypeDesktop,
/** Apps that generally aren't started from the desktop (e.g. image viewer) */

View File

@ -0,0 +1,73 @@
#include <Timer.h>
#include <Check.h>
#include <Thread.h>
#include <Kernel.h>
#include "Assets.h"
#include "app/App.h"
#include "lvgl.h"
#include "service/loader/Loader.h"
#include "lvgl/Style.h"
#ifdef ESP_PLATFORM
#include "sdkconfig.h"
#else
#define CONFIG_TT_SPLASH_DURATION 0
#endif
namespace tt::app::boot {
static int32_t threadCallback(void* context);
struct Data {
Data() : thread("", 4096, threadCallback, this) {}
Thread thread;
};
static int32_t threadCallback(TT_UNUSED void* context) {
TickType_t start_time = tt::get_ticks();
// Do stuff
TickType_t end_time = tt::get_ticks();
TickType_t ticks_passed = end_time - start_time;
TickType_t minimum_ticks = (CONFIG_TT_SPLASH_DURATION / portTICK_PERIOD_MS);
if (minimum_ticks > ticks_passed) {
tt::delay_ticks(minimum_ticks - ticks_passed);
}
tt::service::loader::stopApp();
tt::service::loader::startApp("Desktop");
return 0;
}
static void onShow(TT_UNUSED App& app, lv_obj_t* parent) {
Data* data = (Data*)app.getData();
lv_obj_t* image = lv_image_create(parent);
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
lv_image_set_src(image, TT_ASSETS_BOOT_LOGO);
lvgl::obj_set_style_bg_blacken(parent);
data->thread.start();
}
static void onStart(App& app) {
Data* data = new Data();
app.setData(data);
}
static void onStop(App& app) {
Data* data = (Data*)app.getData();
data->thread.join();
tt_assert(data);
delete data;
}
extern const Manifest manifest = {
.id = "Boot",
.name = "Boot",
.type = TypeBoot,
.onStart = onStart,
.onStop = onStop,
.onShow = onShow,
};
} // namespace

View File

@ -11,7 +11,7 @@ static void on_app_pressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
service::loader::start_app(manifest->id, false, Bundle());
service::loader::startApp(manifest->id, false, Bundle());
}
}

View File

@ -72,7 +72,7 @@ static void on_navigate_up_pressed(lv_event_t* event) {
}
static void on_exit_app_pressed(TT_UNUSED lv_event_t* event) {
service::loader::stop_app();
service::loader::stopApp();
}
static void view_file(const char* path, const char* filename) {
@ -84,7 +84,7 @@ static void view_file(const char* path, const char* filename) {
// For PC we need to make the path relative to the current work directory,
// because that's how LVGL maps its 'drive letter' to the file system.
char* processed_filepath;
if (get_platform() == PlatformPc) {
if (get_platform() == PlatformSimulator) {
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) == nullptr) {
TT_LOG_E(TAG, "Failed to get current working directory");
@ -105,7 +105,7 @@ static void view_file(const char* path, const char* filename) {
if (is_supported_image_file(filename)) {
Bundle bundle;
bundle.putString(IMAGE_VIEWER_FILE_ARGUMENT, processed_filepath);
service::loader::start_app("ImageViewer", false, bundle);
service::loader::startApp("ImageViewer", false, bundle);
} else if (is_supported_text_file(filename)) {
Bundle bundle;
if (get_platform() == PlatformEsp) {
@ -114,7 +114,7 @@ static void view_file(const char* path, const char* filename) {
// Remove forward slash, because we need a relative path
bundle.putString(TEXT_VIEWER_FILE_ARGUMENT, processed_filepath + 1);
}
service::loader::start_app("TextViewer", false, bundle);
service::loader::startApp("TextViewer", false, bundle);
} else {
TT_LOG_W(TAG, "opening files of this type is not supported");
}
@ -199,7 +199,7 @@ static void on_show(App& app, lv_obj_t* parent) {
static void on_start(App& app) {
auto* data = data_alloc();
// PC platform is bound to current work directory because of the LVGL file system mapping
if (get_platform() == PlatformPc) {
if (get_platform() == PlatformSimulator) {
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != nullptr) {
data_set_entries_for_path(data, cwd);

View File

@ -12,7 +12,7 @@ namespace tt::app::screenshot {
static void update_mode(ScreenshotUi* ui) {
lv_obj_t* label = ui->start_stop_button_label;
if (service::screenshot::is_started()) {
if (service::screenshot::isStarted()) {
lv_label_set_text(label, "Stop");
} else {
lv_label_set_text(label, "Start");
@ -34,7 +34,7 @@ static void on_mode_set(lv_event_t* event) {
static void on_start_pressed(lv_event_t* event) {
auto* ui = static_cast<ScreenshotUi*>(lv_event_get_user_data(event));
if (service::screenshot::is_started()) {
if (service::screenshot::isStarted()) {
TT_LOG_I(TAG, "Stop screenshot");
service::screenshot::stop();
} else {
@ -45,13 +45,13 @@ static void on_start_pressed(lv_event_t* event) {
const char* delay_text = lv_textarea_get_text(ui->delay_textarea);
int delay = atoi(delay_text);
if (delay > 0) {
service::screenshot::start_timed(path, delay, 1);
service::screenshot::startTimed(path, delay, 1);
} else {
TT_LOG_W(TAG, "Ignored screenshot start because delay was 0");
}
} else {
TT_LOG_I(TAG, "Start app screenshots");
service::screenshot::start_apps(path);
service::screenshot::startApps(path);
}
}
@ -73,7 +73,7 @@ static void create_mode_setting_ui(ScreenshotUi* ui, lv_obj_t* parent) {
lv_obj_align_to(mode_dropdown, mode_label, LV_ALIGN_OUT_RIGHT_MID, 8, 0);
lv_obj_add_event_cb(mode_dropdown, on_mode_set, LV_EVENT_VALUE_CHANGED, ui);
ui->mode_dropdown = mode_dropdown;
service::screenshot::ScreenshotMode mode = service::screenshot::get_mode();
service::screenshot::Mode mode = service::screenshot::getMode();
if (mode == service::screenshot::ScreenshotModeApps) {
lv_dropdown_set_selected(mode_dropdown, 1);
}
@ -168,8 +168,8 @@ void create_ui(const App& app, ScreenshotUi* ui, lv_obj_t* parent) {
create_path_ui(ui, wrapper);
create_timer_settings_ui(ui, wrapper);
service::gui::keyboard_add_textarea(ui->delay_textarea);
service::gui::keyboard_add_textarea(ui->path_textarea);
service::gui::keyboardAddTextArea(ui->delay_textarea);
service::gui::keyboardAddTextArea(ui->path_textarea);
update_mode(ui);
}

View File

@ -49,11 +49,11 @@ static void onListItemSelected(lv_event_t* e) {
if (code == LV_EVENT_CLICKED) {
size_t index = (size_t)(e->user_data);
TT_LOG_I(TAG, "Selected item at index %d", index);
tt::app::App* app = service::loader::get_current_app();
tt::app::App* app = service::loader::getCurrentApp();
Bundle bundle;
setResultIndex(bundle, (int32_t)index);
app->setResult(app::ResultOk, bundle);
service::loader::stop_app();
service::loader::stopApp();
}
}
@ -79,12 +79,12 @@ static void onShow(App& app, lv_obj_t* parent) {
if (items.empty() || items.front().empty()) {
TT_LOG_E(TAG, "No items provided");
app.setResult(ResultError);
service::loader::stop_app();
service::loader::stopApp();
} else if (items.size() == 1) {
Bundle result_bundle;
setResultIndex(result_bundle, 0);
app.setResult(ResultOk, result_bundle);
service::loader::stop_app();
service::loader::stopApp();
TT_LOG_W(TAG, "Auto-selecting single item");
} else {
size_t index = 0;
@ -95,7 +95,7 @@ static void onShow(App& app, lv_obj_t* parent) {
} else {
TT_LOG_E(TAG, "No items provided");
app.setResult(ResultError);
service::loader::stop_app();
service::loader::stopApp();
}
}

View File

@ -12,7 +12,7 @@ static void on_app_pressed(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
const auto* manifest = static_cast<const Manifest*>(lv_event_get_user_data(e));
service::loader::start_app(manifest->id, false, Bundle());
service::loader::startApp(manifest->id);
}
}

View File

@ -24,7 +24,7 @@ static void on_connect(const service::wifi::settings::WifiApSettings* ap_setting
static WifiConnect* wifi_connect_alloc() {
auto* wifi = static_cast<WifiConnect*>(malloc(sizeof(WifiConnect)));
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &event_callback, wifi);
wifi->mutex = tt_mutex_alloc(MutexTypeNormal);
wifi->state = (WifiConnectState) {
@ -46,7 +46,7 @@ static WifiConnect* wifi_connect_alloc() {
}
static void wifi_connect_free(WifiConnect* wifi) {
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
tt_mutex_free(wifi->mutex);
@ -92,7 +92,7 @@ static void event_callback(const void* message, void* context) {
case service::wifi::WifiEventTypeConnectionSuccess:
if (wifi->state.is_connecting) {
state_set_connecting(wifi, false);
service::loader::stop_app();
service::loader::stopApp();
}
break;
default:

View File

@ -191,8 +191,8 @@ void view_create(const App& app, void* wifi, lv_obj_t* parent) {
view_create_bottom_buttons(wifi_connect, wrapper);
// Keyboard bindings
service::gui::keyboard_add_textarea(view->ssid_textarea);
service::gui::keyboard_add_textarea(view->password_textarea);
service::gui::keyboardAddTextArea(view->ssid_textarea);
service::gui::keyboardAddTextArea(view->password_textarea);
// Init from app parameters
const Bundle& bundle = app.getParameters();

View File

@ -26,7 +26,7 @@ static void on_connect(const char* ssid) {
Bundle bundle;
bundle.putString(WIFI_CONNECT_PARAM_SSID, ssid);
bundle.putString(WIFI_CONNECT_PARAM_PASSWORD, "");
service::loader::start_app("WifiConnect", false, bundle);
service::loader::startApp("WifiConnect", false, bundle);
}
}
@ -35,7 +35,7 @@ static void on_disconnect() {
}
static void on_wifi_toggled(bool enabled) {
service::wifi::set_enabled(enabled);
service::wifi::setEnabled(enabled);
}
static WifiManage* wifi_manage_alloc() {
@ -44,8 +44,8 @@ static WifiManage* wifi_manage_alloc() {
wifi->wifi_subscription = nullptr;
wifi->mutex = tt_mutex_alloc(MutexTypeNormal);
wifi->state = (WifiManageState) {
.scanning = service::wifi::is_scanning(),
.radio_state = service::wifi::get_radio_state(),
.scanning = service::wifi::isScanning(),
.radio_state = service::wifi::getRadioState(),
.connect_ssid = { 0 },
.ap_records = { },
.ap_records_count = 0
@ -94,8 +94,8 @@ void request_view_update(WifiManage* wifi) {
static void wifi_manage_event_callback(const void* message, void* context) {
auto* event = (service::wifi::WifiEvent*)message;
auto* wifi = (WifiManage*)context;
TT_LOG_I(TAG, "Update with state %d", service::wifi::get_radio_state());
state_set_radio_state(wifi, service::wifi::get_radio_state());
TT_LOG_I(TAG, "Update with state %d", service::wifi::getRadioState());
state_set_radio_state(wifi, service::wifi::getRadioState());
switch (event->type) {
case tt::service::wifi::WifiEventTypeScanStarted:
state_set_scanning(wifi, true);
@ -105,7 +105,7 @@ static void wifi_manage_event_callback(const void* message, void* context) {
state_update_scanned_records(wifi);
break;
case tt::service::wifi::WifiEventTypeRadioStateOn:
if (!service::wifi::is_scanning()) {
if (!service::wifi::isScanning()) {
service::wifi::scan();
}
break;
@ -119,12 +119,12 @@ static void wifi_manage_event_callback(const void* message, void* context) {
static void app_show(App& app, lv_obj_t* parent) {
auto* wifi = (WifiManage*)app.getData();
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
wifi->wifi_subscription = tt_pubsub_subscribe(wifi_pubsub, &wifi_manage_event_callback, wifi);
// State update (it has its own locking)
state_set_radio_state(wifi, service::wifi::get_radio_state());
state_set_scanning(wifi, service::wifi::is_scanning());
state_set_radio_state(wifi, service::wifi::getRadioState());
state_set_scanning(wifi, service::wifi::isScanning());
state_update_scanned_records(wifi);
// View update
@ -135,11 +135,11 @@ static void app_show(App& app, lv_obj_t* parent) {
view_update(&wifi->view, &wifi->bindings, &wifi->state);
unlock(wifi);
service::wifi::WifiRadioState radio_state = service::wifi::get_radio_state();
service::wifi::WifiRadioState radio_state = service::wifi::getRadioState();
bool can_scan = radio_state == service::wifi::WIFI_RADIO_ON ||
radio_state == service::wifi::WIFI_RADIO_CONNECTION_PENDING ||
radio_state == service::wifi::WIFI_RADIO_CONNECTION_ACTIVE;
if (can_scan && !service::wifi::is_scanning()) {
if (can_scan && !service::wifi::isScanning()) {
service::wifi::scan();
}
}
@ -147,7 +147,7 @@ static void app_show(App& app, lv_obj_t* parent) {
static void app_hide(App& app) {
auto* wifi = (WifiManage*)app.getData();
lock(wifi);
PubSub* wifi_pubsub = service::wifi::get_pubsub();
PubSub* wifi_pubsub = service::wifi::getPubsub();
tt_pubsub_unsubscribe(wifi_pubsub, wifi->wifi_subscription);
wifi->wifi_subscription = nullptr;
wifi->view_enabled = false;

View File

@ -16,7 +16,7 @@ void state_set_radio_state(WifiManage* wifi, service::wifi::WifiRadioState state
void state_update_scanned_records(WifiManage* wifi) {
lock(wifi);
service::wifi::get_scan_results(
service::wifi::getScanResults(
wifi->state.ap_records,
WIFI_SCAN_AP_RECORD_COUNT,
&wifi->state.ap_records_count

View File

@ -43,7 +43,7 @@ static void connect(lv_event_t* event) {
static void create_network_button(WifiManageView* view, WifiManageBindings* bindings, service::wifi::WifiApRecord* record) {
const char* ssid = (const char*)record->ssid;
const char* icon = service::statusbar::get_status_icon_for_rssi(record->rssi, record->auth_mode != WIFI_AUTH_OPEN);
const char* icon = service::statusbar::getWifiStatusIconForRssi(record->rssi, record->auth_mode != WIFI_AUTH_OPEN);
lv_obj_t* ap_button = lv_list_add_btn(
view->networks_list,
icon,

View File

@ -31,7 +31,7 @@ static const lv_obj_class_t toolbar_class = {
};
static void stop_app(TT_UNUSED lv_event_t* event) {
service::loader::stop_app();
service::loader::stopApp();
}
static void toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) {

View File

@ -1,14 +1,9 @@
#include "Tactility.h"
#include "service/gui/Gui_i.h"
#include "service/loader/Loader.h"
#include "service/loader/Loader_i.h"
#include "lvgl/LvglKeypad.h"
#include "lvgl/LvglSync.h"
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#else
#include "FreeRTOS.h"
#endif
#include "RtosCompat.h"
namespace tt::service::gui {
@ -25,9 +20,9 @@ void loader_callback(const void* message, TT_UNUSED void* context) {
if (event->type == loader::LoaderEventTypeApplicationShowing) {
app::App& app = event->app_showing.app;
const app::Manifest& app_manifest = app.getManifest();
show_app(app, app_manifest.onShow, app_manifest.onHide);
showApp(app, app_manifest.onShow, app_manifest.onHide);
} else if (event->type == loader::LoaderEventTypeApplicationHiding) {
hide_app();
hideApp();
}
}
@ -43,7 +38,7 @@ Gui* gui_alloc() {
);
instance->mutex = tt_mutex_alloc(MutexTypeRecursive);
instance->keyboard = nullptr;
instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader::get_pubsub(), &loader_callback, instance);
instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader::getPubsub(), &loader_callback, instance);
tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS));
instance->keyboard_group = lv_group_create();
instance->lvgl_parent = lv_scr_act();
@ -76,21 +71,21 @@ void unlock() {
tt_check(tt_mutex_release(gui->mutex) == TtStatusOk);
}
void request_draw() {
void requestDraw() {
tt_assert(gui);
ThreadId thread_id = gui->thread->getId();
thread_flags_set(thread_id, GUI_THREAD_FLAG_DRAW);
}
void show_app(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) {
void showApp(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide) {
lock();
tt_check(gui->app_view_port == nullptr);
gui->app_view_port = view_port_alloc(app, on_show, on_hide);
unlock();
request_draw();
requestDraw();
}
void hide_app() {
void hideApp() {
lock();
ViewPort* view_port = gui->app_view_port;
tt_check(view_port != nullptr);

View File

@ -14,26 +14,26 @@ typedef struct Gui Gui;
* @param on_show
* @param on_hide
*/
void show_app(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide);
void showApp(app::App& app, ViewPortShowCallback on_show, ViewPortHideCallback on_hide);
/**
* Hide the current app's viewport.
* Does not request a re-draw because after hiding the current app,
* we always show the previous app, and there is always at least 1 app running.
*/
void hide_app();
void hideApp();
/**
* Show the on-screen keyboard.
* @param textarea the textarea to focus the input for
*/
void keyboard_show(lv_obj_t* textarea);
void keyboardShow(lv_obj_t* textarea);
/**
* Hide the on-screen keyboard.
* Has no effect when the keyboard is not visible.
*/
void keyboard_hide();
void keyboardHide();
/**
* The on-screen keyboard is only shown when both of these conditions are true:
@ -41,7 +41,7 @@ void keyboard_hide();
* - TT_CONFIG_FORCE_ONSCREEN_KEYBOARD is set to true in tactility_config.h
* @return if we should show a on-screen keyboard for text input inside our apps
*/
bool keyboard_is_enabled();
bool keyboardIsEnabled();
/**
* Glue code for the on-screen keyboard and the hardware keyboard:
@ -49,6 +49,6 @@ bool keyboard_is_enabled();
* - Registers the textarea to the default lv_group_t for hardware keyboards.
* @param textarea
*/
void keyboard_add_textarea(lv_obj_t* textarea);
void keyboardAddTextArea(lv_obj_t* textarea);
} // namespace

View File

@ -28,7 +28,7 @@ static lv_obj_t* create_app_views(Gui* gui, lv_obj_t* parent, app::App& app) {
lv_obj_set_width(child_container, LV_PCT(100));
lv_obj_set_flex_grow(child_container, 1);
if (keyboard_is_enabled()) {
if (keyboardIsEnabled()) {
gui->keyboard = lv_keyboard_create(vertical_container);
lv_obj_add_flag(gui->keyboard, LV_OBJ_FLAG_HIDDEN);
} else {

View File

@ -10,19 +10,19 @@ extern Gui* gui;
static void show_keyboard(lv_event_t* event) {
lv_obj_t* target = lv_event_get_current_target_obj(event);
keyboard_show(target);
keyboardShow(target);
lv_obj_scroll_to_view(target, LV_ANIM_ON);
}
static void hide_keyboard(TT_UNUSED lv_event_t* event) {
keyboard_hide();
keyboardHide();
}
bool keyboard_is_enabled() {
bool keyboardIsEnabled() {
return !lvgl::keypad_is_available() || TT_CONFIG_FORCE_ONSCREEN_KEYBOARD;
}
void keyboard_show(lv_obj_t* textarea) {
void keyboardShow(lv_obj_t* textarea) {
lock();
if (gui->keyboard) {
@ -33,7 +33,7 @@ void keyboard_show(lv_obj_t* textarea) {
unlock();
}
void keyboard_hide() {
void keyboardHide() {
lock();
if (gui->keyboard) {
@ -43,11 +43,11 @@ void keyboard_hide() {
unlock();
}
void keyboard_add_textarea(lv_obj_t* textarea) {
void keyboardAddTextArea(lv_obj_t* textarea) {
lock();
tt_check(lvgl::lock(0), "lvgl should already be locked before calling this method");
if (keyboard_is_enabled()) {
if (keyboardIsEnabled()) {
lv_obj_add_event_cb(textarea, show_keyboard, LV_EVENT_FOCUSED, nullptr);
lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_DEFOCUSED, nullptr);
lv_obj_add_event_cb(textarea, hide_keyboard, LV_EVENT_READY, nullptr);

View File

@ -5,14 +5,12 @@
#include "service/Manifest.h"
#include "service/gui/Gui.h"
#include "service/loader/Loader_i.h"
#include "RtosCompat.h"
#ifdef ESP_PLATFORM
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#else
#include "FreeRTOS.h"
#include "lvgl/LvglSync.h"
#endif
namespace tt::service::loader {
@ -65,7 +63,7 @@ static void loader_unlock() {
tt_check(tt_mutex_release(loader_singleton->mutex) == TtStatusOk);
}
LoaderStatus start_app(const std::string& id, bool blocking, const Bundle& bundle) {
LoaderStatus startApp(const std::string& id, bool blocking, const Bundle& arguments) {
TT_LOG_I(TAG, "Start app %s", id.c_str());
tt_assert(loader_singleton);
@ -73,7 +71,7 @@ LoaderStatus start_app(const std::string& id, bool blocking, const Bundle& bundl
.value = LoaderStatusOk
};
auto* start_message = new LoaderMessageAppStart(id, bundle);
auto* start_message = new LoaderMessageAppStart(id, arguments);
LoaderMessage message(start_message, result);
EventFlag* event_flag = blocking ? new EventFlag() : nullptr;
@ -94,14 +92,14 @@ LoaderStatus start_app(const std::string& id, bool blocking, const Bundle& bundl
return result.value;
}
void stop_app() {
void stopApp() {
TT_LOG_I(TAG, "Stop app");
tt_check(loader_singleton);
LoaderMessage message(LoaderMessageTypeAppStop);
loader_singleton->queue.put(&message, TtWaitForever);
}
app::App* _Nullable get_current_app() {
app::App* _Nullable getCurrentApp() {
tt_assert(loader_singleton);
loader_lock();
app::AppInstance* app = loader_singleton->app_stack.top();
@ -109,7 +107,7 @@ app::App* _Nullable get_current_app() {
return dynamic_cast<app::App*>(app);
}
PubSub* get_pubsub() {
PubSub* getPubsub() {
tt_assert(loader_singleton);
// it's safe to return pubsub without locking
// because it's never freed and loader is never exited
@ -160,7 +158,7 @@ static void app_transition_to_state(app::AppInstance& app, app::State state) {
LoaderEvent event_showing = {
.type = LoaderEventTypeApplicationShowing,
.app_showing = {
.app = dynamic_cast<app::App&>(app)
.app = app
}
};
tt_pubsub_publish(loader_singleton->pubsub_external, &event_showing);
@ -171,7 +169,7 @@ static void app_transition_to_state(app::AppInstance& app, app::State state) {
LoaderEvent event_hiding = {
.type = LoaderEventTypeApplicationHiding,
.app_hiding = {
.app = dynamic_cast<app::App&>(app)
.app = app
}
};
tt_pubsub_publish(loader_singleton->pubsub_external, &event_hiding);
@ -198,6 +196,8 @@ static LoaderStatus loader_do_start_app_with_manifest(
auto previous_app = !loader_singleton->app_stack.empty() ? loader_singleton->app_stack.top() : nullptr;
auto new_app = new app::AppInstance(*manifest, bundle);
new_app->mutableFlags().showStatusbar = (manifest->type != app::TypeBoot);
loader_singleton->app_stack.push(new_app);
app_transition_to_state(*new_app, app::StateInitial);
app_transition_to_state(*new_app, app::StateStarted);
@ -217,7 +217,7 @@ static LoaderStatus loader_do_start_app_with_manifest(
LoaderEvent event_external = {
.type = LoaderEventTypeApplicationStarted,
.app_started = {
.app = dynamic_cast<app::App&>(*new_app)
.app = *new_app
}
};
tt_pubsub_publish(loader_singleton->pubsub_external, &event_external);
@ -233,6 +233,7 @@ static LoaderStatus do_start_by_id(
const app::Manifest* manifest = app::findAppById(id);
if (manifest == nullptr) {
TT_LOG_E(TAG, "App not found: %s", id.c_str());
return LoaderStatusErrorUnknownApp;
} else {
return loader_do_start_app_with_manifest(manifest, bundle);
@ -251,14 +252,15 @@ static void do_stop_app() {
return;
}
if (original_stack_size == 1) {
// Stop current app
app::AppInstance* app_to_stop = loader_singleton->app_stack.top();
if (original_stack_size == 1 && app_to_stop->getManifest().type != app::TypeBoot) {
loader_unlock();
TT_LOG_E(TAG, "Stop app: can't stop root app");
return;
}
// Stop current app
app::AppInstance* app_to_stop = loader_singleton->app_stack.top();
std::unique_ptr<app::ResultHolder> result_holder = std::move(app_to_stop->getResult());
const app::Manifest& manifest = app_to_stop->getManifest();
@ -272,35 +274,38 @@ static void do_stop_app() {
TT_LOG_I(TAG, "Free heap: %zu", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
#endif
app::AppInstance* app_to_resume = loader_singleton->app_stack.top();
tt_assert(app_to_resume);
app_transition_to_state(*app_to_resume, app::StateShowing);
// If there's a previous app, resume it
if (!loader_singleton->app_stack.empty()) {
app::AppInstance* app_to_resume = loader_singleton->app_stack.top();
tt_assert(app_to_resume);
app_transition_to_state(*app_to_resume, app::StateShowing);
auto on_result = app_to_resume->getManifest().onResult;
if (on_result != nullptr) {
if (result_holder != nullptr) {
Bundle* result_bundle = result_holder->resultData;
if (result_bundle != nullptr) {
on_result(
*app_to_resume,
result_holder->result,
*result_bundle
);
auto on_result = app_to_resume->getManifest().onResult;
if (on_result != nullptr) {
if (result_holder != nullptr) {
Bundle* result_bundle = result_holder->resultData;
if (result_bundle != nullptr) {
on_result(
*app_to_resume,
result_holder->result,
*result_bundle
);
} else {
const Bundle empty_bundle;
on_result(
*app_to_resume,
result_holder->result,
empty_bundle
);
}
} else {
const Bundle empty_bundle;
on_result(
*app_to_resume,
result_holder->result,
empty_bundle
*app_to_resume,
app::ResultCancelled,
empty_bundle
);
}
} else {
const Bundle empty_bundle;
on_result(
*app_to_resume,
app::ResultCancelled,
empty_bundle
);
}
}

View File

@ -16,58 +16,26 @@ typedef enum {
LoaderStatusErrorInternal,
} LoaderStatus;
typedef enum {
LoaderEventTypeApplicationStarted,
LoaderEventTypeApplicationShowing,
LoaderEventTypeApplicationHiding,
LoaderEventTypeApplicationStopped
} LoaderEventType;
typedef struct {
app::App& app;
} LoaderEventAppStarted;
typedef struct {
app::App& app;
} LoaderEventAppShowing;
typedef struct {
app::App& app;
} LoaderEventAppHiding;
typedef struct {
const app::Manifest& manifest;
} LoaderEventAppStopped;
typedef struct {
LoaderEventType type;
union {
LoaderEventAppStarted app_started;
LoaderEventAppShowing app_showing;
LoaderEventAppHiding app_hiding;
LoaderEventAppStopped app_stopped;
};
} LoaderEvent;
/**
* @brief Start an app
* @param[in] id application name or id
* @param[in] blocking application arguments
* @param[in] bundle optional bundle. Ownership is transferred to Loader.
* @param[in] blocking whether this call is blocking or not. You cannot call this from an LVGL thread.
* @param[in] arguments optional parameters to pass onto the application
* @return LoaderStatus
*/
LoaderStatus start_app(const std::string& id, bool blocking, const Bundle& bundle);
LoaderStatus startApp(const std::string& id, bool blocking = false, const Bundle& arguments = Bundle());
/**
* @brief Stop the currently showing app. Show the previous app if any app was still running.
*/
void stop_app();
void stopApp();
app::App* _Nullable get_current_app();
app::App* _Nullable getCurrentApp();
/**
* @brief PubSub for LoaderEvent
*/
PubSub* get_pubsub();
PubSub* getPubsub();
} // namespace

View File

@ -15,8 +15,8 @@ extern const Manifest manifest;
typedef struct {
Mutex* mutex;
ScreenshotTask* task;
ScreenshotMode mode;
task::ScreenshotTask* task;
Mode mode;
} ServiceData;
static ServiceData* service_data_alloc() {
@ -49,14 +49,14 @@ static void on_start(Service& service) {
static void on_stop(Service& service) {
auto* data = static_cast<ServiceData*>(service.getData());
if (data->task) {
task_free(data->task);
task::free(data->task);
data->task = nullptr;
}
tt_mutex_free(data->mutex);
service_data_free(data);
}
void start_apps(const char* path) {
void startApps(const char* path) {
_Nullable auto* service = findServiceById(manifest.id);
if (service == nullptr) {
TT_LOG_E(TAG, "Service not found");
@ -66,16 +66,16 @@ void start_apps(const char* path) {
auto* data = static_cast<ServiceData*>(service->getData());
service_data_lock(data);
if (data->task == nullptr) {
data->task = task_alloc();
data->task = task::alloc();
data->mode = ScreenshotModeApps;
task_start_apps(data->task, path);
task::startApps(data->task, path);
} else {
TT_LOG_E(TAG, "Screenshot task already running");
}
service_data_unlock(data);
}
void start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount) {
void startTimed(const char* path, uint8_t delay_in_seconds, uint8_t amount) {
_Nullable auto* service = findServiceById(manifest.id);
if (service == nullptr) {
TT_LOG_E(TAG, "Service not found");
@ -85,9 +85,9 @@ void start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount) {
auto* data = static_cast<ServiceData*>(service->getData());
service_data_lock(data);
if (data->task == nullptr) {
data->task = task_alloc();
data->task = task::alloc();
data->mode = ScreenshotModeTimed;
task_start_timed(data->task, path, delay_in_seconds, amount);
task::startTimed(data->task, path, delay_in_seconds, amount);
} else {
TT_LOG_E(TAG, "Screenshot task already running");
}
@ -104,8 +104,8 @@ void stop() {
auto data = static_cast<ServiceData*>(service->getData());
service_data_lock(data);
if (data->task != nullptr) {
task_stop(data->task);
task_free(data->task);
task::stop(data->task);
task::free(data->task);
data->task = nullptr;
data->mode = ScreenshotModeNone;
} else {
@ -114,7 +114,7 @@ void stop() {
service_data_unlock(data);
}
ScreenshotMode get_mode() {
Mode getMode() {
_Nullable auto* service = findServiceById(manifest.id);
if (service == nullptr) {
TT_LOG_E(TAG, "Service not found");
@ -122,14 +122,14 @@ ScreenshotMode get_mode() {
} else {
auto* data = static_cast<ServiceData*>(service->getData());
service_data_lock(data);
ScreenshotMode mode = data->mode;
Mode mode = data->mode;
service_data_unlock(data);
return mode;
}
}
bool is_started() {
return get_mode() != ScreenshotModeNone;
bool isStarted() {
return getMode() != ScreenshotModeNone;
}
extern const Manifest manifest = {

View File

@ -8,24 +8,24 @@ typedef enum {
ScreenshotModeNone,
ScreenshotModeTimed,
ScreenshotModeApps
} ScreenshotMode;
} Mode;
/** @brief Starts taking screenshot with a timer
* @param path the path to store the screenshots in
* @param delay_in_seconds the delay before starting (and between successive screenshots)
* @param amount 0 = indefinite, >0 for a specific
*/
void start_timed(const char* path, uint8_t delay_in_seconds, uint8_t amount);
void startTimed(const char* path, uint8_t delay_in_seconds, uint8_t amount);
/** @brief Starts taking screenshot when an app is started
* @param path the path to store the screenshots in
*/
void start_apps(const char* path);
void startApps(const char* path);
void stop();
ScreenshotMode get_mode();
Mode getMode();
bool is_started();
bool isStarted();
} // namespace

View File

@ -8,7 +8,7 @@
#include "service/loader/Loader.h"
#include "lvgl/LvglSync.h"
namespace tt::service::screenshot {
namespace tt::service::screenshot::task {
#define TAG "screenshot_task"
@ -39,7 +39,7 @@ static void task_unlock(ScreenshotTaskData* data) {
tt_check(tt_mutex_release(data->mutex) == TtStatusOk);
}
ScreenshotTask* task_alloc() {
ScreenshotTask* alloc() {
auto* data = static_cast<ScreenshotTaskData*>(malloc(sizeof(ScreenshotTaskData)));
*data = (ScreenshotTaskData) {
.thread = nullptr,
@ -49,10 +49,10 @@ ScreenshotTask* task_alloc() {
return data;
}
void task_free(ScreenshotTask* task) {
void free(ScreenshotTask* task) {
auto* data = static_cast<ScreenshotTaskData*>(task);
if (data->thread) {
task_stop(data);
stop(data);
}
}
@ -98,7 +98,7 @@ static int32_t screenshot_task(void* context) {
break; // Interrupted loop
}
} else if (data->work.type == TASK_WORK_TYPE_APPS) {
app::App* _Nullable app = loader::get_current_app();
app::App* _Nullable app = loader::getCurrentApp();
if (app) {
const app::Manifest& manifest = app->getManifest();
if (manifest.id != last_app_id) {
@ -136,7 +136,7 @@ static void task_start(ScreenshotTaskData* data) {
task_unlock(data);
}
void task_start_apps(ScreenshotTask* task, const char* path) {
void startApps(ScreenshotTask* task, const char* path) {
tt_check(strlen(path) < (SCREENSHOT_PATH_LIMIT - 1));
auto* data = static_cast<ScreenshotTaskData*>(task);
task_lock(data);
@ -151,7 +151,7 @@ void task_start_apps(ScreenshotTask* task, const char* path) {
task_unlock(data);
}
void task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount) {
void startTimed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount) {
tt_check(strlen(path) < (SCREENSHOT_PATH_LIMIT - 1));
auto* data = static_cast<ScreenshotTaskData*>(task);
task_lock(data);
@ -168,7 +168,7 @@ void task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_s
task_unlock(data);
}
void task_stop(ScreenshotTask* task) {
void stop(ScreenshotTask* task) {
auto* data = static_cast<ScreenshotTaskData*>(task);
if (data->thread != nullptr) {
task_lock(data);

View File

@ -2,13 +2,13 @@
#include <cstdint>
namespace tt::service::screenshot {
namespace tt::service::screenshot::task {
typedef void ScreenshotTask;
ScreenshotTask* task_alloc();
ScreenshotTask* alloc();
void task_free(ScreenshotTask* task);
void free(ScreenshotTask* task);
/** @brief Start taking screenshots after a certain delay
* @param task the screenshot task
@ -16,17 +16,17 @@ void task_free(ScreenshotTask* task);
* @param delay_in_seconds the delay before starting (and between successive screenshots)
* @param amount 0 = indefinite, >0 for a specific
*/
void task_start_timed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount);
void startTimed(ScreenshotTask* task, const char* path, uint8_t delay_in_seconds, uint8_t amount);
/** @brief Start taking screenshot whenever an app is started
* @param task the screenshot task
* @param path the path to store the screenshots at
*/
void task_start_apps(ScreenshotTask* task, const char* path);
void startApps(ScreenshotTask* task, const char* path);
/** @brief Stop taking screenshots
* @param task the screenshot task
*/
void task_stop(ScreenshotTask* task);
void stop(ScreenshotTask* task);
}

View File

@ -25,7 +25,7 @@ typedef struct {
// region wifi
const char* get_status_icon_for_rssi(int rssi, bool secured) {
const char* getWifiStatusIconForRssi(int rssi, bool secured) {
if (rssi > 0) {
return TT_ASSETS_ICON_WIFI_CONNECTION_ISSUE;
} else if (rssi >= -30) {
@ -52,16 +52,16 @@ static const char* wifi_get_status_icon(wifi::WifiRadioState state, bool secure)
case wifi::WIFI_RADIO_CONNECTION_PENDING:
return TT_ASSETS_ICON_WIFI_FIND;
case wifi::WIFI_RADIO_CONNECTION_ACTIVE:
rssi = wifi::get_rssi();
return get_status_icon_for_rssi(rssi, secure);
rssi = wifi::getRssi();
return getWifiStatusIconForRssi(rssi, secure);
default:
tt_crash("not implemented");
}
}
static void update_wifi_icon(ServiceData* data) {
wifi::WifiRadioState radio_state = wifi::get_radio_state();
bool is_secure = wifi::is_connection_secure();
wifi::WifiRadioState radio_state = wifi::getRadioState();
bool is_secure = wifi::isConnectionSecure();
const char* desired_icon = wifi_get_status_icon(radio_state, is_secure);
if (data->wifi_last_icon != desired_icon) {
lvgl::statusbar_icon_set_image(data->wifi_icon_id, desired_icon);

View File

@ -8,6 +8,6 @@ namespace tt::service::statusbar {
* @param secured whether the access point is a secured one (as in: not an open one)
* @return
*/
const char* get_status_icon_for_rssi(int rssi, bool secured);
const char* getWifiStatusIconForRssi(int rssi, bool secured);
} // namespace

View File

@ -2,14 +2,7 @@
#include "CoreDefines.h"
#include "Log.h"
#ifdef ESP_TARGET
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#else
#include "FreeRTOS.h"
#include "task.h"
#endif
#include "RtosCompatTask.h"
#define TAG "kernel"

View File

@ -1,14 +1,7 @@
#pragma once
#include "CoreTypes.h"
#ifdef ESP_TARGET
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#else
#include "FreeRTOS.h"
#include "event_groups.h"
#endif
#include "RtosCompatEventGroups.h"
namespace tt {

View File

@ -2,14 +2,7 @@
#include "Check.h"
#include "CoreDefines.h"
#include "CoreTypes.h"
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#else
#include "FreeRTOS.h"
#include "task.h"
#endif
#include "RtosCompatTask.h"
#ifdef ESP_PLATFORM
#include "rom/ets_sys.h"
@ -118,7 +111,7 @@ uint32_t kernel_get_tick_frequency() {
return (configTICK_RATE_HZ);
}
void delay_tick(uint32_t ticks) {
void delay_ticks(TickType_t ticks) {
tt_assert(!kernel_is_irq());
if (ticks == 0U) {
taskYIELD();
@ -127,7 +120,7 @@ void delay_tick(uint32_t ticks) {
}
}
TtStatus delay_until_tick(uint32_t tick) {
TtStatus delay_until_tick(TickType_t tick) {
tt_assert(!kernel_is_irq());
TickType_t tcnt, delay;
@ -154,7 +147,7 @@ TtStatus delay_until_tick(uint32_t tick) {
return (stat);
}
uint32_t get_tick() {
TickType_t get_ticks() {
TickType_t ticks;
if (kernel_is_irq() != 0U) {
@ -166,11 +159,11 @@ uint32_t get_tick() {
return ticks;
}
uint32_t ms_to_ticks(uint32_t milliseconds) {
TickType_t ms_to_ticks(uint32_t milliseconds) {
#if configTICK_RATE_HZ == 1000
return milliseconds;
return (TickType_t)milliseconds;
#else
return (uint32_t)((float)configTICK_RATE_HZ) / 1000.0f * (float)milliseconds;
return (TickType_t)((float)configTICK_RATE_HZ) / 1000.0f * (float)milliseconds;
#endif
}
@ -182,7 +175,7 @@ void delay_ms(uint32_t milliseconds) {
#if configTICK_RATE_HZ_RAW == 1000
tt_delay_tick(milliseconds);
#else
delay_tick(ms_to_ticks(milliseconds));
delay_ticks(ms_to_ticks(milliseconds));
#endif
} else if (milliseconds > 0) {
delay_us(milliseconds * 1000);
@ -201,7 +194,7 @@ Platform get_platform() {
#ifdef ESP_PLATFORM
return PlatformEsp;
#else
return PlatformPc;
return PlatformSimulator;
#endif
}

View File

@ -2,11 +2,17 @@
#include "CoreTypes.h"
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#else
#include "FreeRTOS.h"
#endif
namespace tt {
typedef enum {
PlatformEsp,
PlatformPc
PlatformSimulator
} Platform;
/** Check if CPU is in IRQ or kernel running and IRQ is masked
@ -64,6 +70,8 @@ int32_t kernel_restore_lock(int32_t lock);
*/
uint32_t kernel_get_tick_frequency();
TickType_t get_ticks();
/** Delay execution
*
* @warning This should never be called in interrupt request context.
@ -72,7 +80,7 @@ uint32_t kernel_get_tick_frequency();
*
* @param[in] ticks The ticks count to pause
*/
void delay_tick(uint32_t ticks);
void delay_ticks(TickType_t ticks);
/** Delay until tick
*
@ -89,7 +97,7 @@ TtStatus delay_until_tick(uint32_t tick);
* @param[in] milliseconds time in milliseconds
* @return time in ticks
*/
uint32_t ms_to_ticks(uint32_t milliseconds);
TickType_t ms_to_ticks(uint32_t milliseconds);
/** Delay in milliseconds
*

View File

@ -6,14 +6,7 @@
#include "CoreTypes.h"
#include "Thread.h"
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#else
#include "FreeRTOS.h"
#include "semphr.h"
#endif
#include "RtosCompatSemaphore.h"
namespace tt {

View File

@ -0,0 +1,13 @@
#pragma once
/**
* Compatibility includes for FreeRTOS.
* Custom FreeRTOS from ESP-IDF prefixes paths with "freertos/",
* but this isn't the normal behaviour for the regular FreeRTOS project.
*/
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#else
#include "FreeRTOS.h"
#endif

View File

@ -0,0 +1,14 @@
#pragma once
/**
* See explanation in RtosCompat.h
*/
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#else
#include "FreeRTOS.h"
#include "event_groups.h"
#endif

View File

@ -0,0 +1,14 @@
#pragma once
/**
* See explanation in RtosCompat.h
*/
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#else
#include "FreeRTOS.h"
#include "semphr.h"
#endif

View File

@ -0,0 +1,14 @@
#pragma once
/**
* See explanation in RtosCompat.h
*/
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#else
#include "FreeRTOS.h"
#include "task.h"
#endif

View File

@ -0,0 +1,13 @@
#pragma once
/**
* See explanation in RtosCompat.h
*/
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
#else
#include "FreeRTOS.h"
#include "timers.h"
#endif

View File

@ -1,6 +1,7 @@
#include "Timer.h"
#include "Check.h"
#include "Kernel.h"
#include "RtosCompat.h"
namespace tt {
@ -73,7 +74,7 @@ uint32_t Timer::getExpireTime() {
return (uint32_t)xTimerGetExpiryTime(timerHandle);
}
void Timer::pendingCallback(PendigCallback callback, void* callbackContext, uint32_t arg) {
void Timer::pendingCallback(PendingCallback callback, void* callbackContext, uint32_t arg) {
BaseType_t ret = pdFAIL;
if (kernel_is_irq()) {
ret = xTimerPendFunctionCallFromISR(callback, callbackContext, arg, nullptr);

View File

@ -2,24 +2,17 @@
#include "CoreTypes.h"
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
#else
#include "FreeRTOS.h"
#include "timers.h"
#endif
#include "RtosCompatTimers.h"
namespace tt {
class Timer {
private:
TimerHandle_t timerHandle;
public:
typedef void (*Callback)(void* context);
typedef void (*PendigCallback)(void* context, uint32_t arg);
typedef void (*PendingCallback)(void* context, uint32_t arg);
Callback callback;
@ -88,7 +81,7 @@ public:
*/
uint32_t getExpireTime();
void pendingCallback(PendigCallback callback, void* callbackContext, uint32_t arg);
void pendingCallback(PendingCallback callback, void* callbackContext, uint32_t arg);
typedef enum {
TimerThreadPriorityNormal, /**< Lower then other threads */
@ -102,9 +95,4 @@ public:
void setThreadPriority(TimerThreadPriority priority);
};
} // namespace

View File

@ -1,13 +1,6 @@
#include "Critical.h"
#include "CoreDefines.h"
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#else
#include "FreeRTOS.h"
#include "task.h"
#endif
#include "RtosCompatTask.h"
#ifdef ESP_PLATFORM
static portMUX_TYPE critical_mutex;

View File

@ -3,6 +3,9 @@
#define TT_ASSET_FOLDER "A:/assets/"
#define TT_ASSET(file) TT_ASSET_FOLDER file
// Splash
#define TT_ASSETS_BOOT_LOGO TT_ASSET("boot_logo.png")
// App icons
#define TT_ASSETS_APP_ICON_FALLBACK TT_ASSET("app_icon_fallback.png")
#define TT_ASSETS_APP_ICON_FILES TT_ASSET("app_icon_files.png")

View File

@ -2,16 +2,11 @@
#include "I2cCompat.h"
#include "CoreTypes.h"
#include "RtosCompat.h"
#include <climits>
#include <string>
#include <vector>
#ifdef ESP_TARGET
#include "freertos/FreeRTOS.h"
#else
#include "FreeRTOS.h"
#endif
namespace tt::hal::i2c {
typedef enum {

View File

@ -74,9 +74,9 @@ typedef struct {
* @brief Get wifi pubsub
* @return PubSub*
*/
PubSub* get_pubsub();
PubSub* getPubsub();
WifiRadioState get_radio_state();
WifiRadioState getRadioState();
/**
* @brief Request scanning update. Returns immediately. Results are through pubsub.
*/
@ -85,26 +85,26 @@ void scan();
/**
* @return true if wifi is actively scanning
*/
bool is_scanning();
bool isScanning();
/**
* @brief Returns the access points from the last scan (if any). It only contains public APs.
* @param records the allocated buffer to store the records in
* @param limit the maximum amount of records to store
*/
void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count);
void getScanResults(WifiApRecord records[], uint16_t limit, uint16_t* result_count);
/**
* @brief Overrides the default scan result size of 16.
* @param records the record limit for the scan result (84 bytes per record!)
*/
void set_scan_records(uint16_t records);
void setScanRecords(uint16_t records);
/**
* @brief Enable/disable the radio. Ignores input if desired state matches current state.
* @param enabled
*/
void set_enabled(bool enabled);
void setEnabled(bool enabled);
/**
* @brief Connect to a network. Disconnects any existing connection.
@ -121,11 +121,11 @@ void disconnect();
/**
* Return true if the connection isn't unencrypted.
*/
bool is_connection_secure();
bool isConnectionSecure();
/**
* Returns the RSSI value (negative number) or return 1 when not connected
*/
int get_rssi();
int getRssi();
} // namespace

View File

@ -99,12 +99,12 @@ Wifi::~Wifi() {
// region Public functions
PubSub* get_pubsub() {
PubSub* getPubsub() {
tt_assert(wifi_singleton);
return wifi_singleton->pubsub;
}
WifiRadioState get_radio_state() {
WifiRadioState getRadioState() {
tt_assert(wifi_singleton);
lock(wifi_singleton);
WifiRadioState state = wifi_singleton->radio_state;
@ -121,7 +121,7 @@ void scan() {
unlock(wifi_singleton);
}
bool is_scanning() {
bool isScanning() {
tt_assert(wifi_singleton);
lock(wifi_singleton);
bool is_scanning = wifi_singleton->scan_active;
@ -152,7 +152,7 @@ void disconnect() {
unlock(wifi_singleton);
}
void set_scan_records(uint16_t records) {
void setScanRecords(uint16_t records) {
tt_assert(wifi_singleton);
lock(wifi_singleton);
if (records != wifi_singleton->scan_list_limit) {
@ -162,7 +162,7 @@ void set_scan_records(uint16_t records) {
unlock(wifi_singleton);
}
void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) {
void getScanResults(WifiApRecord records[], uint16_t limit, uint16_t* result_count) {
tt_assert(wifi_singleton);
tt_assert(result_count);
@ -185,7 +185,7 @@ void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_c
unlock(wifi_singleton);
}
void set_enabled(bool enabled) {
void setEnabled(bool enabled) {
tt_assert(wifi_singleton);
lock(wifi_singleton);
if (enabled) {
@ -200,7 +200,7 @@ void set_enabled(bool enabled) {
unlock(wifi_singleton);
}
bool is_connection_secure() {
bool isConnectionSecure() {
tt_assert(wifi_singleton);
lock(wifi_singleton);
bool is_secure = wifi_singleton->secure_connection;
@ -208,7 +208,7 @@ bool is_connection_secure() {
return is_secure;
}
int get_rssi() {
int getRssi() {
tt_assert(wifi_singleton);
static int rssi = 0;
if (esp_wifi_sta_get_rssi(&rssi) == ESP_OK) {

View File

@ -67,12 +67,12 @@ static void wifi_free(Wifi* instance) {
// region Public functions
PubSub* get_pubsub() {
PubSub* getPubsub() {
tt_assert(wifi);
return wifi->pubsub;
}
WifiRadioState get_radio_state() {
WifiRadioState getRadioState() {
return wifi->radio_state;
}
@ -81,7 +81,7 @@ void scan() {
wifi->scan_active = false; // TODO: enable and then later disable automatically
}
bool is_scanning() {
bool isScanning() {
tt_assert(wifi);
return wifi->scan_active;
}
@ -95,12 +95,12 @@ void disconnect() {
tt_assert(wifi);
}
void set_scan_records(uint16_t records) {
void setScanRecords(uint16_t records) {
tt_assert(wifi);
// TODO: implement
}
void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) {
void getScanResults(WifiApRecord records[], uint16_t limit, uint16_t* result_count) {
tt_check(wifi);
tt_check(result_count);
@ -126,7 +126,7 @@ void get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_c
}
}
void set_enabled(bool enabled) {
void setEnabled(bool enabled) {
tt_assert(wifi != NULL);
if (enabled) {
wifi->radio_state = WIFI_RADIO_ON;
@ -136,11 +136,11 @@ void set_enabled(bool enabled) {
}
}
bool is_connection_secure() {
bool isConnectionSecure() {
return wifi->secure_connection;
}
int get_rssi() {
int getRssi() {
if (wifi->radio_state == WIFI_RADIO_CONNECTION_ACTIVE) {
return -30;
} else {

View File

@ -14,7 +14,7 @@ TEST_CASE("dispatcher should not call callback if consume isn't called") {
uint32_t counter = 0;
dispatcher.dispatch(&increment_callback, &counter);
delay_tick(10);
delay_ticks(10);
CHECK_EQ(counter, 0);
}

View File

@ -18,7 +18,7 @@ TEST_CASE("a timer passes the context correctly") {
int foo = 1;
auto* timer = new Timer(Timer::TypeOnce, &timer_callback_with_context, &foo);
timer->start(1);
delay_tick(10);
delay_ticks(10);
timer->stop();
delete timer;
@ -29,10 +29,10 @@ TEST_CASE("TimerTypePeriodic timers can be stopped and restarted") {
int counter = 0;
auto* timer = new Timer(Timer::TypePeriodic, &timer_callback_with_counter, &counter);
timer->start(1);
delay_tick(10);
delay_ticks(10);
timer->stop();
timer->start(1);
delay_tick(10);
delay_ticks(10);
timer->stop();
delete timer;
@ -44,7 +44,7 @@ TEST_CASE("TimerTypePeriodic calls the callback periodically") {
int ticks_to_run = 10;
auto* timer = new Timer(Timer::TypePeriodic, &timer_callback_with_counter, &counter);
timer->start(1);
delay_tick(ticks_to_run);
delay_ticks(ticks_to_run);
timer->stop();
delete timer;
@ -55,10 +55,10 @@ TEST_CASE("restarting TimerTypeOnce timers calls the callback again") {
int counter = 0;
auto* timer = new Timer(Timer::TypeOnce, &timer_callback_with_counter, &counter);
timer->start(1);
delay_tick(10);
delay_ticks(10);
timer->stop();
timer->start(1);
delay_tick(10);
delay_ticks(10);
timer->stop();
delete timer;

View File

@ -13,6 +13,7 @@ CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
CONFIG_FREERTOS_SMP=n
CONFIG_FREERTOS_UNICORE=n
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"

View File

@ -13,7 +13,7 @@ CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
CONFIG_FREERTOS_SMP=n
CONFIG_FREERTOS_UNICORE=n
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
@ -25,6 +25,7 @@ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_FLASHMODE_QIO=y
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
# Hardware: SPI RAM
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y

View File

@ -13,7 +13,7 @@ CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
CONFIG_FREERTOS_SMP=n
CONFIG_FREERTOS_UNICORE=n
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
@ -25,6 +25,7 @@ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_FLASHMODE_QIO=y
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
# Hardware: SPI RAM
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_QUAD=y

View File

@ -13,6 +13,7 @@ CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
CONFIG_FREERTOS_SMP=n
CONFIG_FREERTOS_UNICORE=n
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"

View File

@ -13,6 +13,7 @@ CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2
CONFIG_FREERTOS_SMP=n
CONFIG_FREERTOS_UNICORE=n
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"

4
sdkconfig.developer Normal file
View File

@ -0,0 +1,4 @@
CONFIG_STACK_CHECK_STRONG=y
LV_USE_SYSMON=y
CONFIG_LV_USE_OBSERVER=y
CONFIG_LV_USE_PERF_MONITOR=y