Filesystem improvements and more (#148)

- Rename `assets` and `config` partitions to `system` and `data`
- Change partition type from `spiffs` to `fat`, so we can have sub-directories
- Fix crash when doing WiFi scan: Increased system event task size to 3kB. 
- Free up IRAM on ESP32 (it was required for the Core2, but I also freed up the same amount for Yellow Board)
- Introduced `Paths` objects that can be retrieved by `AppContext` and `ServiceContext`. Apps and services now have their own relative paths. Assets were re-arranged into the correct paths.
- Rename simulator window title to "Tactility"
- Refactored statusbar widget so it persists icon paths properly (it kept a const char* reference, but didn't copy it, so it crashed when the related std::string was destroyed)
- Created `Partitions.h` to expose some useful variables
- Moved USB config in various `sdkconfig`  (it was part of the "default" section, but it shouldn't be)
- Updated domain name
This commit is contained in:
Ken Van Hoeylandt 2025-01-05 20:44:33 +01:00 committed by GitHub
parent 7187e5e49e
commit ff4287e2ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
107 changed files with 592 additions and 259 deletions

View File

@ -82,6 +82,7 @@ static void lvgl_task(TT_UNUSED void* arg) {
* but somehow that doesn't work. Waiting here from a ThreadFlag when that happens
* also doesn't work. It seems that it must be called from this task. */
displayHandle = lv_sdl_window_create(320, 240);
lv_sdl_window_set_title(displayHandle, "Tactility");
uint32_t task_delay_ms = task_max_sleep_ms;

2
Data/data/test.txt Normal file
View File

@ -0,0 +1,2 @@
This file exists to test the partition.
It can be deleted when the partition contains other files.

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 688 B

View File

Before

Width:  |  Height:  |  Size: 564 B

After

Width:  |  Height:  |  Size: 564 B

View File

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 724 B

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 350 B

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 360 B

View File

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 505 B

View File

Before

Width:  |  Height:  |  Size: 333 B

After

Width:  |  Height:  |  Size: 333 B

View File

Before

Width:  |  Height:  |  Size: 240 B

After

Width:  |  Height:  |  Size: 240 B

View File

Before

Width:  |  Height:  |  Size: 286 B

After

Width:  |  Height:  |  Size: 286 B

View File

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 429 B

View File

Before

Width:  |  Height:  |  Size: 299 B

After

Width:  |  Height:  |  Size: 299 B

View File

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 535 B

View File

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 436 B

View File

Before

Width:  |  Height:  |  Size: 142 B

After

Width:  |  Height:  |  Size: 142 B

View File

Before

Width:  |  Height:  |  Size: 144 B

After

Width:  |  Height:  |  Size: 144 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 146 B

View File

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 146 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 149 B

After

Width:  |  Height:  |  Size: 149 B

View File

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 193 B

View File

Before

Width:  |  Height:  |  Size: 196 B

After

Width:  |  Height:  |  Size: 196 B

View File

Before

Width:  |  Height:  |  Size: 394 B

After

Width:  |  Height:  |  Size: 394 B

View File

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 407 B

View File

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

View File

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 517 B

View File

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 534 B

View File

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 384 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-160q-33 0-56.5-23.5T160-240q0-33 23.5-56.5T240-320q33 0 56.5 23.5T320-240q0 33-23.5 56.5T240-160Zm240 0q-33 0-56.5-23.5T400-240q0-33 23.5-56.5T480-320q33 0 56.5 23.5T560-240q0 33-23.5 56.5T480-160Zm240 0q-33 0-56.5-23.5T640-240q0-33 23.5-56.5T720-320q33 0 56.5 23.5T800-240q0 33-23.5 56.5T720-160ZM240-400q-33 0-56.5-23.5T160-480q0-33 23.5-56.5T240-560q33 0 56.5 23.5T320-480q0 33-23.5 56.5T240-400Zm240 0q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm240 0q-33 0-56.5-23.5T640-480q0-33 23.5-56.5T720-560q33 0 56.5 23.5T800-480q0 33-23.5 56.5T720-400ZM240-640q-33 0-56.5-23.5T160-720q0-33 23.5-56.5T240-800q33 0 56.5 23.5T320-720q0 33-23.5 56.5T240-640Zm240 0q-33 0-56.5-23.5T400-720q0-33 23.5-56.5T480-800q33 0 56.5 23.5T560-720q0 33-23.5 56.5T480-640Zm240 0q-33 0-56.5-23.5T640-720q0-33 23.5-56.5T720-800q33 0 56.5 23.5T800-720q0 33-23.5 56.5T720-640Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H447l-80-80H160v480Zm0 0v-480 480Z"/></svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>

After

Width:  |  Height:  |  Size: 771 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 369 B

After

Width:  |  Height:  |  Size: 369 B

View File

Before

Width:  |  Height:  |  Size: 514 B

After

Width:  |  Height:  |  Size: 514 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -10,6 +10,7 @@
- Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount)
- Clean up static_cast when casting to base class.
- M5Stack CoreS3 SD card mounts, but cannot be read. There is currently a notice about it [here](https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_core_s3/README.md).
- SD card statusbar icon shows error when there's a read timeout on the SD card status. Don't show the error icon in this scenario.
# TODOs
- Call tt::lvgl::isSyncSet after HAL init and show error (and crash?) when it is not set.

View File

@ -2,7 +2,7 @@
Tactility is an operating system that focuses on the ESP32 microcontroller family.
See [https://tactility.bytewelder.com](https://tactility.bytewelder.com) for more information.
See [https://tactility.one](https://tactility.one) for more information.
Status: pre-release

View File

@ -82,6 +82,9 @@ public:
void setResult(Result result) override;
void setResult(Result result, std::shared_ptr<const Bundle> bundle) override;
bool hasResult() const override;
std::unique_ptr<Paths> getPaths() const override;
std::unique_ptr<ResultHolder>& getResult() { return resultHolder; }
};

View File

@ -0,0 +1,28 @@
#pragma once
#include "app/AppInstance.h"
namespace tt::app {
class AppInstancePaths final : public Paths {
private:
const AppManifest& manifest;
public:
explicit AppInstancePaths(const AppManifest& manifest) : manifest(manifest) {}
~AppInstancePaths() final = default;
std::string getDataDirectory() const final;
std::string getDataDirectoryLvgl() const final;
std::string getDataPath(const std::string& childPath) const final;
std::string getDataPathLvgl(const std::string& childPath) const final;
std::string getSystemDirectory() const final;
std::string getSystemDirectoryLvgl() const final;
std::string getSystemPath(const std::string& childPath) const final;
std::string getSystemPathLvgl(const std::string& childPath) const final;
};
}

View File

@ -6,6 +6,8 @@
namespace tt::app {
class Paths;
typedef union {
struct {
bool showStatusbar : 1;
@ -33,6 +35,68 @@ public:
virtual void setResult(Result result) = 0;
virtual void setResult(Result result, std::shared_ptr<const Bundle> bundle)= 0;
virtual bool hasResult() const = 0;
virtual std::unique_ptr<Paths> getPaths() const = 0;
};
class Paths {
public:
Paths() = default;
virtual ~Paths() = default;
/**
* Returns the directory path for the data location for an app.
* The data directory is intended to survive OS upgrades.
* The path will not end with a "/".
*/
virtual std::string getDataDirectory() const = 0;
/**
* @see getDataDirectory(), but with LVGL prefix.
*/
virtual std::string getDataDirectoryLvgl() const = 0;
/**
* Returns the full path for an entry inside the data location for an app.
* The data directory is intended to survive OS upgrades.
* Configuration data should be stored here.
* @param[in] childPath the path without a "/" prefix
*/
virtual std::string getDataPath(const std::string& childPath) const = 0;
/**
* @see getDataPath(), but with LVGL prefix.
*/
virtual std::string getDataPathLvgl(const std::string& childPath) const = 0;
/**
* Returns the directory path for the system location for an app.
* The system directory is not intended to survive OS upgrades.
* You should not store configuration data here.
* The path will not end with a "/".
* This is mainly used for core apps (system/boot/settings type).
*/
virtual std::string getSystemDirectory() const = 0;
/**
* @see getSystemDirectory(), but with LVGL prefix.
*/
virtual std::string getSystemDirectoryLvgl() const = 0;
/**
* Returns the full path for an entry inside the system location for an app.
* The data directory is not intended to survive OS upgrades.
* You should not store configuration data here.
* This is mainly used for core apps (system/boot/settings type).
* @param[in] childPath the path without a "/" prefix
*/
virtual std::string getSystemPath(const std::string& childPath) const = 0;
/**
* @see getSystemPath(), but with LVGL prefix.
*/
virtual std::string getSystemPathLvgl(const std::string& childPath) const = 0;
};
}

View File

@ -1,4 +1,5 @@
#include "app/AppInstance.h"
#include "app/AppInstancePaths.h"
namespace tt::app {
@ -81,4 +82,8 @@ bool AppInstance::hasResult() const {
return has_result;
}
std::unique_ptr<Paths> AppInstance::getPaths() const {
return std::make_unique<AppInstancePaths>(manifest);
}
} // namespace

View File

@ -0,0 +1,48 @@
#include "app/AppInstancePaths.h"
#include "Partitions.h"
#define LVGL_PATH_PREFIX std::string("A:/")
#ifdef ESP_PLATFORM
#define PARTITION_PREFIX std::string("/")
#else
#define PARTITION_PREFIX std::string("")
#endif
namespace tt::app {
std::string AppInstancePaths::getDataDirectory() const {
return PARTITION_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id;
}
std::string AppInstancePaths::getDataDirectoryLvgl() const {
return LVGL_PATH_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id;
}
std::string AppInstancePaths::getDataPath(const std::string& childPath) const {
assert(!childPath.starts_with('/'));
return PARTITION_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
}
std::string AppInstancePaths::getDataPathLvgl(const std::string& childPath) const {
assert(!childPath.starts_with('/'));
return LVGL_PATH_PREFIX + DATA_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
}
std::string AppInstancePaths::getSystemDirectory() const {
return PARTITION_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id;
}
std::string AppInstancePaths::getSystemDirectoryLvgl() const {
return LVGL_PATH_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id;
}
std::string AppInstancePaths::getSystemPath(const std::string& childPath) const {
assert(!childPath.starts_with('/'));
return PARTITION_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
}
std::string AppInstancePaths::getSystemPathLvgl(const std::string& childPath) const {
return LVGL_PATH_PREFIX + SYSTEM_PARTITION_NAME + "/app/" + manifest.id + '/' + childPath;
}
}

View File

@ -18,7 +18,7 @@
#define CONFIG_TT_SPLASH_DURATION 0
#endif
#define TAG "Boot"
#define TAG "boot"
namespace tt::app::boot {
@ -90,11 +90,11 @@ static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
lv_obj_t* image = lv_image_create(parent);
lv_obj_set_size(image, LV_PCT(100), LV_PCT(100));
if (hal::usb::isUsbBootMode()) {
lv_image_set_src(image, TT_ASSETS_BOOT_LOGO_USB);
} else {
lv_image_set_src(image, TT_ASSETS_BOOT_LOGO);
}
auto paths = app.getPaths();
const char* logo = hal::usb::isUsbBootMode() ? "logo_usb.png" : "logo.png";
auto logo_path = paths->getSystemPathLvgl(logo);
TT_LOG_I(TAG, "%s", logo_path.c_str());
lv_image_set_src(image, logo_path.c_str());
lvgl::obj_set_style_bg_blacken(parent);

View File

@ -25,9 +25,8 @@ std::string getUrlFromCrashData() {
std::stringstream stream;
stream << "https://oops.bytewelder.com?";
stream << "i=1"; // Application id
stream << "&v=" << TT_VERSION; // Version
stream << "https://oops.tactility.one";
stream << "?v=" << TT_VERSION; // Version
stream << "&a=" << CONFIG_IDF_TARGET; // Architecture
stream << "&s="; // Stacktrace

View File

@ -1,7 +1,9 @@
#include "app/AppContext.h"
#include "app/ManifestRegistry.h"
#include "Check.h"
#include "lvgl.h"
#include "service/loader/Loader.h"
#include "Assets.h"
namespace tt::app::desktop {
@ -61,9 +63,13 @@ static void onShow(TT_UNUSED AppContext& app, lv_obj_t* parent) {
int32_t available_width = lv_display_get_horizontal_resolution(display) - (3 * 80);
int32_t padding = TT_MIN(available_width / 4, 64);
createAppButton(wrapper, "Apps", "A:/assets/desktop_icon_apps.png", "AppList", 0);
createAppButton(wrapper, "Files", "A:/assets/desktop_icon_files.png", "Files", padding);
createAppButton(wrapper, "Settings", "A:/assets/desktop_icon_settings.png", "Settings", padding);
auto paths = app.getPaths();
auto apps_icon_path = paths->getSystemPathLvgl("icon_apps.png");
auto files_icon_path = paths->getSystemPathLvgl("icon_files.png");
auto settings_icon_path = paths->getSystemPathLvgl("icon_settings.png");
createAppButton(wrapper, "Apps", apps_icon_path.c_str(), "AppList", 0);
createAppButton(wrapper, "Files", files_icon_path.c_str(), "Files", padding);
createAppButton(wrapper, "Settings", settings_icon_path.c_str(), "Settings", padding);
}
extern const AppManifest manifest = {

View File

@ -36,6 +36,7 @@ int scandir(
ScandirFilter _Nullable filterMethod,
ScandirSort _Nullable sortMethod
) {
TT_LOG_I(TAG, "scandir start");
DIR* dir = opendir(path.c_str());
if (dir == nullptr) {
TT_LOG_E(TAG, "Failed to open dir %s", path.c_str());
@ -55,6 +56,7 @@ int scandir(
sort(outList.begin(), outList.end(), sortMethod);
}
TT_LOG_I(TAG, "scandir finish");
return (int)outList.size();
};

View File

@ -2,6 +2,8 @@
#include "kernel/Kernel.h"
#include "Log.h"
#include "FileUtils.h"
#include "Partitions.h"
#include "hal/SdCard.h"
#include <unistd.h>
@ -51,17 +53,17 @@ bool State::setEntriesForPath(const std::string& path) {
dir_entries.push_back({
.d_ino = 0,
.d_type = TT_DT_DIR,
.d_name = "assets"
.d_name = SYSTEM_PARTITION_NAME
});
dir_entries.push_back({
.d_ino = 1,
.d_type = TT_DT_DIR,
.d_name = "config"
.d_name = DATA_PARTITION_NAME
});
dir_entries.push_back({
.d_ino = 2,
.d_type = TT_DT_DIR,
.d_name = "sdcard"
.d_name = TT_SDCARD_MOUNT_NAME
});
current_path = path;

View File

@ -24,11 +24,16 @@ static void onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
auto parameters = app.getParameters();
tt_check(parameters != nullptr, "Parameters missing");
bool success = false;
std::string file_argument;
if (parameters->optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) {
TT_LOG_I(TAG, "Opening %s", file_argument.c_str());
lvgl::label_set_text_file(label, file_argument.c_str());
} else {
if (lvgl::label_set_text_file(label, file_argument.c_str())) {
success = true;
}
}
if (!success) {
lv_label_set_text_fmt(label, "Failed to load %s", file_argument.c_str());
}
}

View File

@ -2,7 +2,6 @@
#include "WifiManage.h"
#include "Log.h"
#include "Assets.h"
#include "service/wifi/Wifi.h"
#include "lvgl/Style.h"
#include "lvgl/Toolbar.h"
@ -19,11 +18,11 @@ std::shared_ptr<WifiManage> _Nullable optWifiManage();
const char* getWifiStatusIconForRssi(int rssi) {
if (rssi >= -60) {
return TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_BLACK;
return "signal_strong.png";
} else if (rssi >= -70) {
return TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_BLACK;
return "signal_medium.png";
} else {
return TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_BLACK;
return "signal_weak.png";
}
}
@ -111,13 +110,15 @@ void View::createSsidListItem(const service::wifi::WifiApRecord& record, bool is
lv_obj_align_to(connecting_spinner, info_wrapper, LV_ALIGN_OUT_LEFT_MID, -8, 0);
} else {
const char* icon = getWifiStatusIconForRssi(record.rssi);
auto icon_path = paths->getSystemPathLvgl(icon);
lv_obj_t* rssi_image = lv_image_create(wrapper);
lv_image_set_src(rssi_image, icon);
lv_image_set_src(rssi_image, icon_path.c_str());
lv_obj_align(rssi_image, LV_ALIGN_RIGHT_MID, -42, 0);
if (record.auth_mode != WIFI_AUTH_OPEN) {
lv_obj_t* lock_image = lv_image_create(wrapper);
lv_image_set_src(lock_image, TT_ASSETS_ICON_WIFI_LOCK_BLACK);
auto lock = paths->getSystemPathLvgl("lock.png");
lv_image_set_src(lock_image, lock.c_str());
lv_obj_align(lock_image, LV_ALIGN_RIGHT_MID, -62, 0);
}
}
@ -232,6 +233,8 @@ void View::updateEnableOnBootToggle() {
void View::init(const AppContext& app, lv_obj_t* parent) {
root = parent;
paths = std::move(app.getPaths());
// Toolbar
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_t* toolbar = lvgl::toolbar_create(parent, app);

View File

@ -13,6 +13,7 @@ private:
Bindings* bindings;
State* state;
std::unique_ptr<app::Paths> paths;
lv_obj_t* root = nullptr;
lv_obj_t* enable_switch = nullptr;
lv_obj_t* enable_on_boot_switch = nullptr;

View File

@ -5,9 +5,14 @@ namespace tt::lvgl {
#define TAG "tt_lv_label"
void label_set_text_file(lv_obj_t* label, const char* filepath) {
bool label_set_text_file(lv_obj_t* label, const char* filepath) {
auto text = file::readString(filepath);
lv_label_set_text(label, (const char*)text.get());
if (text != nullptr) {
lv_label_set_text(label, (const char*)text.get());
return true;
} else {
return false;
}
}
} // namespace

View File

@ -4,6 +4,6 @@
namespace tt::lvgl {
void label_set_text_file(lv_obj_t* label, const char* filepath);
bool label_set_text_file(lv_obj_t* label, const char* filepath);
} // namespace

View File

@ -1,13 +0,0 @@
#include "Spacer.h"
#include "Style.h"
namespace tt::lvgl {
lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height) {
lv_obj_t* spacer = lv_obj_create(parent);
lv_obj_set_size(spacer, width, height);
obj_set_style_bg_invisible(spacer);
return spacer;
}
} // namespace

View File

@ -1,10 +0,0 @@
#pragma once
#include "lvgl.h"
namespace tt::lvgl {
[[deprecated("Use margin")]]
lv_obj_t* spacer_create(lv_obj_t* parent, int32_t width, int32_t height);
} // namespace

View File

@ -4,7 +4,6 @@
#include "Mutex.h"
#include "Pubsub.h"
#include "TactilityCore.h"
#include "lvgl/Spacer.h"
#include "lvgl/Style.h"
#include "LvglSync.h"
@ -15,7 +14,7 @@ namespace tt::lvgl {
#define TAG "statusbar"
struct StatusbarIcon {
const char* image = nullptr;
std::string image;
bool visible = false;
bool claimed = false;
};
@ -89,8 +88,8 @@ static void statusbar_destructor(TT_UNUSED const lv_obj_class_t* class_p, lv_obj
}
static void update_icon(lv_obj_t* image, const StatusbarIcon* icon) {
if (icon->image != nullptr && icon->visible && icon->claimed) {
lv_image_set_src(image, icon->image);
if (!icon->image.empty() && icon->visible && icon->claimed) {
lv_image_set_src(image, icon->image.c_str());
lv_obj_remove_flag(image, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(image, LV_OBJ_FLAG_HIDDEN);
@ -110,7 +109,9 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
lv_obj_center(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_t* left_spacer = spacer_create(obj, 1, 1);
lv_obj_t* left_spacer = lv_obj_create(obj);
lv_obj_set_size(left_spacer, 1, 1);
obj_set_style_bg_invisible(left_spacer);
lv_obj_set_flex_grow(left_spacer, 1);
statusbar_lock(TtWaitForever);
@ -154,13 +155,13 @@ static void statusbar_event(TT_UNUSED const lv_obj_class_t* class_p, lv_event_t*
}
}
int8_t statusbar_icon_add(const char* _Nullable image) {
int8_t statusbar_icon_add(const std::string& image) {
statusbar_lock(TtWaitForever);
int8_t result = -1;
for (int8_t i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
if (!statusbar_data.icons[i].claimed) {
statusbar_data.icons[i].claimed = true;
statusbar_data.icons[i].visible = (image != nullptr);
statusbar_data.icons[i].visible = !image.empty();
statusbar_data.icons[i].image = image;
result = i;
TT_LOG_I(TAG, "id %d: added", i);
@ -172,6 +173,10 @@ int8_t statusbar_icon_add(const char* _Nullable image) {
return result;
}
int8_t statusbar_icon_add() {
return statusbar_icon_add("");
}
void statusbar_icon_remove(int8_t id) {
TT_LOG_I(TAG, "id %d: remove", id);
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
@ -179,13 +184,13 @@ void statusbar_icon_remove(int8_t id) {
StatusbarIcon* icon = &statusbar_data.icons[id];
icon->claimed = false;
icon->visible = false;
icon->image = nullptr;
icon->image = "";
tt_pubsub_publish(statusbar_data.pubsub, nullptr);
statusbar_unlock();
}
void statusbar_icon_set_image(int8_t id, const char* image) {
TT_LOG_I(TAG, "id %d: set image %s", id, image ? image : "(none)");
void statusbar_icon_set_image(int8_t id, const std::string& image) {
TT_LOG_I(TAG, "id %d: set image %s", id, image.empty() ? "(none)" : image.c_str());
tt_check(id >= 0 && id < STATUSBAR_ICON_LIMIT);
if (statusbar_lock(50 / portTICK_PERIOD_MS)) {
StatusbarIcon* icon = &statusbar_data.icons[id];

View File

@ -10,9 +10,10 @@ namespace tt::lvgl {
#define STATUSBAR_HEIGHT (STATUSBAR_ICON_SIZE + 4) // 4 extra pixels for border and outline
lv_obj_t* statusbar_create(lv_obj_t* parent);
int8_t statusbar_icon_add(const char* _Nullable image);
int8_t statusbar_icon_add(const std::string& image);
int8_t statusbar_icon_add();
void statusbar_icon_remove(int8_t id);
void statusbar_icon_set_image(int8_t id, const char* image);
void statusbar_icon_set_image(int8_t id, const std::string& image);
void statusbar_icon_set_visibility(int8_t id, bool visible);
} // namespace

View File

@ -3,7 +3,6 @@
#include "Toolbar.h"
#include "service/loader/Loader.h"
#include "lvgl/Spacer.h"
#include "lvgl/Style.h"
#include "Spinner.h"

View File

@ -16,4 +16,5 @@ void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callb
lv_obj_t* toolbar_add_button_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data);
lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj);
lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj);
} // namespace

View File

@ -243,7 +243,7 @@ static void stopAppInternal() {
return;
}
std::unique_ptr<app::ResultHolder> result_holder = std::move(app_to_stop->getResult());
auto result_holder = std::move(app_to_stop->getResult());
const app::AppManifest& manifest = app_to_stop->getManifest();
transitionAppToState(*app_to_stop, app::StateHiding);

View File

@ -1,4 +1,3 @@
#include "Assets.h"
#include "Mutex.h"
#include "Timer.h"
#include "Tactility.h"
@ -16,18 +15,44 @@ namespace tt::service::statusbar {
#define TAG "statusbar_service"
// SD card status
#define STATUSBAR_ICON_SDCARD "sdcard.png"
#define STATUSBAR_ICON_SDCARD_ALERT "sdcard_alert.png"
// Wifi status
#define STATUSBAR_ICON_WIFI_OFF_WHITE "wifi_off_white.png"
#define STATUSBAR_ICON_WIFI_SCAN_WHITE "wifi_scan_white.png"
#define STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE "wifi_signal_weak_white.png"
#define STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE "wifi_signal_medium_white.png"
#define STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE "wifi_signal_strong_white.png"
// Power status
#define STATUSBAR_ICON_POWER_0 "power_0.png"
#define STATUSBAR_ICON_POWER_10 "power_10.png"
#define STATUSBAR_ICON_POWER_20 "power_20.png"
#define STATUSBAR_ICON_POWER_30 "power_30.png"
#define STATUSBAR_ICON_POWER_40 "power_40.png"
#define STATUSBAR_ICON_POWER_50 "power_50.png"
#define STATUSBAR_ICON_POWER_60 "power_60.png"
#define STATUSBAR_ICON_POWER_70 "power_70.png"
#define STATUSBAR_ICON_POWER_80 "power_80.png"
#define STATUSBAR_ICON_POWER_90 "power_90.png"
#define STATUSBAR_ICON_POWER_100 "power_100.png"
extern const ServiceManifest manifest;
struct ServiceData {
Mutex mutex;
std::unique_ptr<Timer> updateTimer;
int8_t wifi_icon_id = lvgl::statusbar_icon_add(nullptr);
int8_t wifi_icon_id = lvgl::statusbar_icon_add();
const char* wifi_last_icon = nullptr;
int8_t sdcard_icon_id = lvgl::statusbar_icon_add(nullptr);
int8_t sdcard_icon_id = lvgl::statusbar_icon_add();
const char* sdcard_last_icon = nullptr;
int8_t power_icon_id = lvgl::statusbar_icon_add(nullptr);
int8_t power_icon_id = lvgl::statusbar_icon_add();
const char* power_last_icon = nullptr;
std::unique_ptr<service::Paths> paths;
~ServiceData() {
lvgl::statusbar_icon_remove(wifi_icon_id);
lvgl::statusbar_icon_remove(sdcard_icon_id);
@ -47,24 +72,24 @@ struct ServiceData {
const char* getWifiStatusIconForRssi(int rssi) {
if (rssi >= -60) {
return TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_WHITE;
return STATUSBAR_ICON_WIFI_SIGNAL_STRONG_WHITE;
} else if (rssi >= -70) {
return TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_WHITE;
return STATUSBAR_ICON_WIFI_SIGNAL_MEDIUM_WHITE;
} else {
return TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_WHITE;
return STATUSBAR_ICON_WIFI_SIGNAL_WEAK_WHITE;
}
}
static const char* wifi_get_status_icon(wifi::WifiRadioState state, bool secure) {
static const char* getWifiStatusIcon(wifi::WifiRadioState state, bool secure) {
int rssi;
switch (state) {
case wifi::WIFI_RADIO_ON:
case wifi::WIFI_RADIO_ON_PENDING:
case wifi::WIFI_RADIO_CONNECTION_PENDING:
return TT_ASSETS_ICON_WIFI_SCAN_WHITE;
return STATUSBAR_ICON_WIFI_SCAN_WHITE;
case wifi::WIFI_RADIO_OFF_PENDING:
case wifi::WIFI_RADIO_OFF:
return TT_ASSETS_ICON_WIFI_OFF_WHITE;
return STATUSBAR_ICON_WIFI_OFF_WHITE;
case wifi::WIFI_RADIO_CONNECTION_ACTIVE:
rssi = wifi::getRssi();
return getWifiStatusIconForRssi(rssi);
@ -73,12 +98,18 @@ static const char* wifi_get_status_icon(wifi::WifiRadioState state, bool secure)
}
}
static void update_wifi_icon(std::shared_ptr<ServiceData> data) {
static void updateWifiIcon(const service::Paths* paths, const std::shared_ptr<ServiceData>& data) {
wifi::WifiRadioState radio_state = wifi::getRadioState();
bool is_secure = wifi::isConnectionSecure();
const char* desired_icon = wifi_get_status_icon(radio_state, is_secure);
const char* desired_icon = getWifiStatusIcon(radio_state, is_secure);
if (data->wifi_last_icon != desired_icon) {
lvgl::statusbar_icon_set_image(data->wifi_icon_id, desired_icon);
if (desired_icon != nullptr) {
auto icon_path = paths->getSystemPathLvgl(desired_icon);
lvgl::statusbar_icon_set_image(data->wifi_icon_id, icon_path);
lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, true);
} else {
lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, false);
}
data->wifi_last_icon = desired_icon;
}
}
@ -87,27 +118,32 @@ static void update_wifi_icon(std::shared_ptr<ServiceData> data) {
// region sdcard
static const char* sdcard_get_status_icon(hal::SdCard::State state) {
static const char* getSdCardStatusIcon(hal::SdCard::State state) {
switch (state) {
case hal::SdCard::StateMounted:
return TT_ASSETS_ICON_SDCARD;
return STATUSBAR_ICON_SDCARD;
case hal::SdCard::StateError:
case hal::SdCard::StateUnmounted:
case hal::SdCard::StateUnknown:
return TT_ASSETS_ICON_SDCARD_ALERT;
return STATUSBAR_ICON_SDCARD_ALERT;
default:
tt_crash("Unhandled SdCard state");
}
}
static void update_sdcard_icon(std::shared_ptr<ServiceData> data) {
static void updateSdCardIcon(const service::Paths* paths, const std::shared_ptr<ServiceData>& data) {
auto sdcard = tt::hal::getConfiguration().sdcard;
if (sdcard != nullptr) {
auto state = sdcard->getState();
const char* desired_icon = sdcard_get_status_icon(state);
const char* desired_icon = getSdCardStatusIcon(state);
if (data->sdcard_last_icon != desired_icon) {
lvgl::statusbar_icon_set_image(data->sdcard_icon_id, desired_icon);
lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, desired_icon != nullptr);
if (desired_icon != nullptr) {
auto icon_path = paths->getSystemPathLvgl(desired_icon);
lvgl::statusbar_icon_set_image(data->sdcard_icon_id, icon_path);
lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, true);
} else {
lvgl::statusbar_icon_set_visibility(data->sdcard_icon_id, false);
}
data->sdcard_last_icon = desired_icon;
}
}
@ -117,7 +153,7 @@ static void update_sdcard_icon(std::shared_ptr<ServiceData> data) {
// region power
static _Nullable const char* power_get_status_icon() {
static _Nullable const char* getPowerStatusIcon() {
auto get_power = getConfiguration()->hardware->power;
if (get_power == nullptr) {
return nullptr;
@ -133,35 +169,40 @@ static _Nullable const char* power_get_status_icon() {
uint8_t charge = charge_level.valueAsUint8;
if (charge >= 95) {
return TT_ASSETS_ICON_POWER_100;
return STATUSBAR_ICON_POWER_100;
} else if (charge >= 85) {
return TT_ASSETS_ICON_POWER_90;
return STATUSBAR_ICON_POWER_90;
} else if (charge >= 75) {
return TT_ASSETS_ICON_POWER_80;
return STATUSBAR_ICON_POWER_80;
} else if (charge >= 65) {
return TT_ASSETS_ICON_POWER_70;
return STATUSBAR_ICON_POWER_70;
} else if (charge >= 55) {
return TT_ASSETS_ICON_POWER_60;
return STATUSBAR_ICON_POWER_60;
} else if (charge >= 45) {
return TT_ASSETS_ICON_POWER_50;
return STATUSBAR_ICON_POWER_50;
} else if (charge >= 35) {
return TT_ASSETS_ICON_POWER_40;
return STATUSBAR_ICON_POWER_40;
} else if (charge >= 25) {
return TT_ASSETS_ICON_POWER_30;
return STATUSBAR_ICON_POWER_30;
} else if (charge >= 15) {
return TT_ASSETS_ICON_POWER_20;
return STATUSBAR_ICON_POWER_20;
} else if (charge >= 5) {
return TT_ASSETS_ICON_POWER_10;
return STATUSBAR_ICON_POWER_10;
} else {
return TT_ASSETS_ICON_POWER_0;
return STATUSBAR_ICON_POWER_0;
}
}
static void update_power_icon(std::shared_ptr<ServiceData> data) {
const char* desired_icon = power_get_status_icon();
static void updatePowerStatusIcon(const service::Paths* paths, const std::shared_ptr<ServiceData>& data) {
const char* desired_icon = getPowerStatusIcon();
if (data->power_last_icon != desired_icon) {
lvgl::statusbar_icon_set_image(data->power_icon_id, desired_icon);
lvgl::statusbar_icon_set_visibility(data->power_icon_id, desired_icon != nullptr);
if (desired_icon != nullptr) {
auto icon_path = paths->getSystemPathLvgl(desired_icon);
lvgl::statusbar_icon_set_image(data->power_icon_id, icon_path);
lvgl::statusbar_icon_set_visibility(data->power_icon_id, true);
} else {
lvgl::statusbar_icon_set_visibility(data->power_icon_id, false);
}
data->power_last_icon = desired_icon;
}
}
@ -177,9 +218,10 @@ static void service_data_free(ServiceData* data) {
static void onUpdate(std::shared_ptr<void> parameter) {
auto data = std::static_pointer_cast<ServiceData>(parameter);
// TODO: Make thread-safe for LVGL
update_wifi_icon(data);
update_sdcard_icon(data);
update_power_icon(data);
auto* paths = data->paths.get();
updateWifiIcon(paths, data);
updateSdCardIcon(paths, data);
updatePowerStatusIcon(paths, data);
}
static void onStart(ServiceContext& service) {
@ -187,13 +229,15 @@ static void onStart(ServiceContext& service) {
auto data = std::make_shared<ServiceData>();
lvgl::unlock();
data->paths = std::move(service.getPaths());
service.setData(data);
// TODO: Make thread-safe for LVGL
lvgl::statusbar_icon_set_visibility(data->wifi_icon_id, true);
update_wifi_icon(data);
update_sdcard_icon(data); // also updates visibility
update_power_icon(data);
updateWifiIcon(data->paths.get(), data);
updateSdCardIcon(data->paths.get(), data); // also updates visibility
updatePowerStatusIcon(data->paths.get(), data);
data->updateTimer = std::make_unique<Timer>(Timer::TypePeriodic, onUpdate, data);
// We want to try and scan more often in case of startup or scan lock failure

View File

@ -4,7 +4,7 @@
namespace tt {
#define TAG "Dispatcher"
#define TAG "dispatcher"
#define BACKPRESSURE_WARNING_COUNT 100
#define WAIT_FLAG 1

View File

@ -118,7 +118,7 @@ static uint64_t getTimestamp() {
void log(LogLevel level, const char* tag, const char* format, ...) {
std::stringstream buffer;
buffer << toColour(level) << toPrefix(level) << " (" << getTimestamp() << ") " << tag << " " << format << "\033[0m\n";
buffer << toColour(level) << toPrefix(level) << " (" << getTimestamp() << ") " << tag << ": " << format << "\033[0m\n";
va_list args;
va_start(args, format);

View File

@ -49,7 +49,7 @@ static std::unique_ptr<uint8_t[]> readBinaryInternal(const std::string& filepath
size_t buffer_offset = 0;
while (buffer_offset < content_length) {
size_t bytes_read = fread(&data.get()[buffer_offset], 1, content_length - buffer_offset, file);
TT_LOG_I(TAG, "Read %d bytes", bytes_read);
TT_LOG_D(TAG, "Read %d bytes", bytes_read);
if (bytes_read > 0) {
buffer_offset += bytes_read;
} else { // Something went wrong?
@ -71,11 +71,15 @@ std::unique_ptr<uint8_t[]> readBinary(const std::string& filepath, size_t& outSi
std::unique_ptr<uint8_t[]> readString(const std::string& filepath) {
size_t size = 0;
auto data = readBinaryInternal(filepath, size, 1);
if (size > 0) {
if (data == nullptr) {
return nullptr;
} else if (size > 0) {
data.get()[size] = 0; // Append null terminator
return data;
} else {
return nullptr;
} else { // Empty file: return empty string
auto value = std::make_unique<uint8_t[]>(1);
value[0] = 0;
return value;
}
}

View File

@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (DEFINED ENV{ESP_IDF_VERSION})
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
list(APPEND REQUIRES_LIST TactilityCore esp_wifi nvs_flash driver spiffs vfs fatfs )
list(APPEND REQUIRES_LIST TactilityCore esp_wifi nvs_flash driver spiffs vfs fatfs)
if("${IDF_TARGET}" STREQUAL "esp32s3")
list(APPEND REQUIRES_LIST esp_tinyusb)
endif()
@ -19,10 +19,10 @@ if (DEFINED ENV{ESP_IDF_VERSION})
)
if (NOT DEFINED TACTILITY_SKIP_SPIFFS)
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/assets")
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT)
set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../Data/config")
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
# Read-only
fatfs_create_rawflash_image(system "${CMAKE_CURRENT_SOURCE_DIR}/../Data/system" FLASH_IN_PROJECT PRESERVE_TIME)
# Read-write
fatfs_create_spiflash_image(data "${CMAKE_CURRENT_SOURCE_DIR}/../Data/data" FLASH_IN_PROJECT PRESERVE_TIME)
endif()
add_definitions(-DESP_PLATFORM)

View File

@ -6,7 +6,7 @@
namespace tt {
esp_err_t initEspPartitions();
esp_err_t initPartitionsEsp();
} // namespace

View File

@ -20,6 +20,8 @@ public:
const service::ServiceManifest& getManifest() const override;
std::shared_ptr<void> getData() const override;
void setData(std::shared_ptr<void> newData) override;
std::unique_ptr<Paths> getPaths() const override;
};
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "service/ServiceInstance.h"
namespace tt::service {
class ServiceInstancePaths final : public Paths {
private:
const ServiceManifest& manifest;
public:
explicit ServiceInstancePaths(const ServiceManifest& manifest) : manifest(manifest) {}
~ServiceInstancePaths() final = default;
std::string getDataDirectory() const final;
std::string getDataDirectoryLvgl() const final;
std::string getDataPath(const std::string& childPath) const final;
std::string getDataPathLvgl(const std::string& childPath) const final;
std::string getSystemDirectory() const final;
std::string getSystemDirectoryLvgl() const final;
std::string getSystemPath(const std::string& childPath) const final;
std::string getSystemPathLvgl(const std::string& childPath) const final;
};
}

View File

@ -1,12 +1,8 @@
#pragma once
#define TT_ASSET_FOLDER "A:/assets/"
#define TT_ASSET_FOLDER "A:/system/"
#define TT_ASSET(file) TT_ASSET_FOLDER file
// Splash
#define TT_ASSETS_BOOT_LOGO TT_ASSET("boot_logo.png")
#define TT_ASSETS_BOOT_LOGO_USB TT_ASSET("boot_logo_usb.png")
// UI
#define TT_ASSETS_UI_SPINNER TT_ASSET("spinner.png")
@ -18,32 +14,3 @@
#define TT_ASSETS_APP_ICON_I2C_SETTINGS TT_ASSET("app_icon_i2c.png")
#define TT_ASSETS_APP_ICON_SETTINGS TT_ASSET("app_icon_settings.png")
#define TT_ASSETS_APP_ICON_SYSTEM_INFO TT_ASSET("app_icon_system_info.png")
// SD card status
#define TT_ASSETS_ICON_SDCARD TT_ASSET("sdcard.png")
#define TT_ASSETS_ICON_SDCARD_ALERT TT_ASSET("sdcard_alert.png")
// Wifi status
#define TT_ASSETS_ICON_WIFI_OFF_WHITE TT_ASSET("wifi_off_white.png")
#define TT_ASSETS_ICON_WIFI_SCAN_WHITE TT_ASSET("wifi_scan_white.png")
#define TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_WHITE TT_ASSET("wifi_signal_weak_white.png")
#define TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_WHITE TT_ASSET("wifi_signal_medium_white.png")
#define TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_WHITE TT_ASSET("wifi_signal_strong_white.png")
// Black (Wifi Manage)
#define TT_ASSETS_ICON_WIFI_LOCK_BLACK TT_ASSET("wifi_lock_black.png")
#define TT_ASSETS_ICON_WIFI_SIGNAL_WEAK_BLACK TT_ASSET("wifi_signal_weak_black.png")
#define TT_ASSETS_ICON_WIFI_SIGNAL_MEDIUM_BLACK TT_ASSET("wifi_signal_medium_black.png")
#define TT_ASSETS_ICON_WIFI_SIGNAL_STRONG_BLACK TT_ASSET("wifi_signal_strong_black.png")
// Power status
#define TT_ASSETS_ICON_POWER_0 TT_ASSET("power_0.png")
#define TT_ASSETS_ICON_POWER_10 TT_ASSET("power_10.png")
#define TT_ASSETS_ICON_POWER_20 TT_ASSET("power_20.png")
#define TT_ASSETS_ICON_POWER_30 TT_ASSET("power_30.png")
#define TT_ASSETS_ICON_POWER_40 TT_ASSET("power_40.png")
#define TT_ASSETS_ICON_POWER_50 TT_ASSET("power_50.png")
#define TT_ASSETS_ICON_POWER_60 TT_ASSET("power_60.png")
#define TT_ASSETS_ICON_POWER_70 TT_ASSET("power_70.png")
#define TT_ASSETS_ICON_POWER_80 TT_ASSET("power_80.png")
#define TT_ASSETS_ICON_POWER_90 TT_ASSET("power_90.png")
#define TT_ASSETS_ICON_POWER_100 TT_ASSET("power_100.png")

View File

@ -1,76 +0,0 @@
#ifdef ESP_TARGET
#include "EspPartitions.h"
#include "EspPartitions_i.h"
#include "Log.h"
#include "esp_spiffs.h"
#include "nvs_flash.h"
namespace tt {
static const char* TAG = "filesystem";
static esp_err_t initNvsFlashSafely() {
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 initSpiffs(esp_vfs_spiffs_conf_t* conf) {
esp_err_t ret = esp_vfs_spiffs_register(conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
TT_LOG_E(TAG, "Failed to mount or format filesystem %s", conf->base_path);
} else if (ret == ESP_ERR_NOT_FOUND) {
TT_LOG_E(TAG, "Failed to find SPIFFS partition %s", conf->base_path);
} else {
TT_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) {
TT_LOG_E(TAG, "Failed to get SPIFFS partition information for %s (%s)", conf->base_path, esp_err_to_name(ret));
} else {
TT_LOG_I(TAG, "Partition size for %s: total: %d, used: %d", conf->base_path, total, used);
}
return ESP_OK;
}
esp_err_t initEspPartitions() {
ESP_ERROR_CHECK(initNvsFlashSafely());
esp_vfs_spiffs_conf_t assets_spiffs = {
.base_path = MOUNT_POINT_ASSETS,
.partition_label = NULL,
.max_files = 100,
.format_if_mount_failed = false
};
if (initSpiffs(&assets_spiffs) != ESP_OK) {
return ESP_FAIL;
}
esp_vfs_spiffs_conf_t config_spiffs = {
.base_path = MOUNT_POINT_CONFIG,
.partition_label = "config",
.max_files = 100,
.format_if_mount_failed = false
};
if (initSpiffs(&config_spiffs) != ESP_OK) {
return ESP_FAIL;
}
return ESP_OK;
}
} // namespace
#endif // ESP_TARGET

View File

@ -1,12 +0,0 @@
#pragma once
#ifdef ESP_TARGET
namespace tt {
#define MOUNT_POINT_ASSETS "/assets"
#define MOUNT_POINT_CONFIG "/config"
} // namespace
#endif // ESP_TARGET

View File

@ -0,0 +1,21 @@
#pragma once
namespace tt {
#define SYSTEM_PARTITION_NAME "system"
#ifdef ESP_PLATFORM
#define MOUNT_POINT_SYSTEM "/system"
#else
#define MOUNT_POINT_SYSTEM "system"
#endif
#define DATA_PARTITION_NAME "data"
#ifdef ESP_PLATFORM
#define MOUNT_POINT_DATA "/data"
#else
#define MOUNT_POINT_DATA "data"
#endif
} // namespace

View File

@ -0,0 +1,54 @@
#ifdef ESP_TARGET
#include "EspPartitions_i.h"
#include "Log.h"
#include <esp_vfs_fat.h>
#include <nvs_flash.h>
namespace tt {
static const char* TAG = "partitions";
static esp_err_t initNvsFlashSafely() {
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 wl_handle_t data_wl_handle = WL_INVALID_HANDLE;
esp_err_t initPartitionsEsp() {
ESP_ERROR_CHECK(initNvsFlashSafely());
const esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 4,
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE,
.disk_status_check_enable = false,
.use_one_fat = true,
};
auto system_result = esp_vfs_fat_spiflash_mount_ro("/system", "system", &mount_config);
if (system_result != ESP_OK) {
TT_LOG_E(TAG, "Failed to mount /system (%s)", esp_err_to_name(system_result));
} else {
TT_LOG_I(TAG, "Mounted /system");
}
auto data_result = esp_vfs_fat_spiflash_mount_rw_wl("/data", "data", &mount_config, &data_wl_handle);
if (data_result != ESP_OK) {
TT_LOG_E(TAG, "Failed to mount /data (%s)", esp_err_to_name(data_result));
} else {
TT_LOG_I(TAG, "Mounted /data");
}
return system_result == ESP_OK && data_result == ESP_OK;
}
} // namespace
#endif // ESP_TARGET

View File

@ -1,7 +1,5 @@
#include "TactilityCore.h"
#ifdef ESP_TARGET
#include "TactilityCore.h"
#include "EspPartitions_i.h"
#include "esp_event.h"
@ -13,7 +11,7 @@ namespace tt {
#define TAG "tactility"
// Initialize NVS
static void initEspNvs() {
static void initNvs() {
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
TT_LOG_I(TAG, "nvs erasing");
@ -24,15 +22,15 @@ static void initEspNvs() {
TT_LOG_I(TAG, "nvs initialized");
}
static void initEspNetwork() {
static void initNetwork() {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
}
void initEsp() {
initEspNvs();
initEspPartitions();
initEspNetwork();
initNvs();
initPartitionsEsp();
initNetwork();
}
} // namespace

View File

@ -19,7 +19,7 @@ void init(const Configuration& configuration) {
if (configuration.sdcard != nullptr) {
TT_LOG_I(TAG, "Mounting sdcard");
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT )) {
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT)) {
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
}
}

View File

@ -4,6 +4,7 @@
namespace tt::hal {
#define TT_SDCARD_MOUNT_NAME "sdcard"
#define TT_SDCARD_MOUNT_POINT "/sdcard"
class SdCard {

View File

@ -6,6 +6,8 @@
namespace tt::service {
class Paths;
class ServiceContext {
protected:
@ -17,6 +19,68 @@ public:
virtual const service::ServiceManifest& getManifest() const = 0;
virtual std::shared_ptr<void> getData() const = 0;
virtual void setData(std::shared_ptr<void> newData) = 0;
virtual std::unique_ptr<Paths> getPaths() const = 0;
};
class Paths {
public:
Paths() = default;
virtual ~Paths() = default;
/**
* Returns the directory path for the data location for a service.
* The data directory is intended to survive OS upgrades.
* The path will not end with a "/".
*/
virtual std::string getDataDirectory() const = 0;
/**
* @see getDataDirectory(), but with LVGL prefix.
*/
virtual std::string getDataDirectoryLvgl() const = 0;
/**
* Returns the full path for an entry inside the data location for a service.
* The data directory is intended to survive OS upgrades.
* Configuration data should be stored here.
* @param[in] childPath the path without a "/" prefix
*/
virtual std::string getDataPath(const std::string& childPath) const = 0;
/**
* @see getDataPath(), but with LVGL prefix.
*/
virtual std::string getDataPathLvgl(const std::string& childPath) const = 0;
/**
* Returns the directory path for the system location for a service.
* The system directory is not intended to survive OS upgrades.
* You should not store configuration data here.
* The path will not end with a "/".
* This is mainly used for core services.
*/
virtual std::string getSystemDirectory() const = 0;
/**
* @see getSystemDirectory(), but with LVGL prefix.
*/
virtual std::string getSystemDirectoryLvgl() const = 0;
/**
* Returns the full path for an entry inside the system location for an app.
* The data directory is not intended to survive OS upgrades.
* You should not store configuration data here.
* This is mainly used for core apps (system/boot/settings type).
* @param[in] childPath the path without a "/" prefix
*/
virtual std::string getSystemPath(const std::string& childPath) const = 0;
/**
* @see getSystemPath(), but with LVGL prefix.
*/
virtual std::string getSystemPathLvgl(const std::string& childPath) const = 0;
};
} // namespace

View File

@ -1,6 +1,7 @@
#include <utility>
#include "service/ServiceInstance.h"
#include "service/ServiceInstancePaths.h"
namespace tt::service {
@ -21,4 +22,8 @@ void ServiceInstance::setData(std::shared_ptr<void> newData) {
mutex.release();
}
std::unique_ptr<Paths> ServiceInstance::getPaths() const {
return std::make_unique<ServiceInstancePaths>(manifest);
}
}

Some files were not shown because too many files have changed in this diff Show More