Merge develop into main (#304)
## New - Read property files with `PropertiesFile` - Support `boot.properties` so the user can specify the launcher app and an optional app to start after the launcher finishes. (see `BootProperties.cpp`) - Create registry for CPU affinity and update code to make use of it - `AppRegistration` and `ServiceRegistration` now also ensure that the `/data` directories always exist for all apps - `Notes` is now the default app for opening text files. `TextViewer` is removed entirely. Created `tt::app::notes::start(path)` function. - WiFi settings moved from NVS to properties file. - Specify `*.ap.properties` file on the SD card for automatic WiFi settings import on start-up. - Added `file::getLock(path)` and `file::withLock(path, function)` to do safe file operations on SD cards ## Improvements - Update TinyUSB to `1.7.6~1` - Improved `Boot.cpp` code. General code quality fixes and some restructuring to improve readability. - `tt::string` functionality improvements - Rename `AppRegistry` to `AppRegistration` - Rename `ServiceRegistry` to `ServiceRegistration` - Cleanup in `Notes.cpp` - `FileTest.cpp` fix for PC - Created `TestFile` helper class for tests, which automatically deletes files after the test. - Renamed `Partitions.h` to `MountPoints.h` - Created `std::string getMountPoints()` function for easy re-use - Other code quality improvements - `SdCardDevice`'s `getState()` and `isMounted()` now have a timeout argument ## Fixes - ELF loading now has a lock so to avoid a bug when 2 ELF apps are loaded in parallel
This commit is contained in:
committed by
GitHub
parent
fbaff8cbac
commit
ee5a5a7181
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tt {
|
||||
|
||||
struct BootProperties {
|
||||
/** App to start automatically after the splash screen. */
|
||||
std::string launcherAppId;
|
||||
/** App to start automatically from the launcher screen. */
|
||||
std::string autoStartAppId;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the boot properties from the relevant file location(s).
|
||||
* It will first attempt to load them from the SD card and if no file was found,
|
||||
* then it will try to load the one from the data mount point.
|
||||
*
|
||||
* @param[out] properties the resulting properties
|
||||
* @return true when the properties were successfully loaded and the result was set
|
||||
*/
|
||||
bool loadBootProperties(BootProperties& properties);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <vector>
|
||||
|
||||
namespace tt::file {
|
||||
|
||||
constexpr auto* SYSTEM_PARTITION_NAME = "system";
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
constexpr auto* MOUNT_POINT_SYSTEM = "/system";
|
||||
#else
|
||||
constexpr auto* MOUNT_POINT_SYSTEM = "system";
|
||||
#endif
|
||||
|
||||
constexpr auto* DATA_PARTITION_NAME = "data";
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
constexpr auto* MOUNT_POINT_DATA = "/data";
|
||||
#else
|
||||
constexpr auto* MOUNT_POINT_DATA = "data";
|
||||
#endif
|
||||
|
||||
std::vector<dirent> getMountPoints();
|
||||
|
||||
} // namespace
|
||||
@@ -1,21 +0,0 @@
|
||||
#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
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <Tactility/TactilityConfig.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
@@ -20,10 +19,6 @@ struct Configuration {
|
||||
const std::vector<const app::AppManifest*> apps = {};
|
||||
/** List of user services */
|
||||
const std::vector<const service::ServiceManifest*> services = {};
|
||||
/** Optional app to start automatically after the splash screen. */
|
||||
const std::string launcherAppId = app::launcher::manifest.id;
|
||||
/** Optional app to start automatically after the splash screen. */
|
||||
const std::string autoStartAppId = {};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tactility/app/ManifestRegistry.h"
|
||||
|
||||
#include <Tactility/CoreDefines.h>
|
||||
#include <Tactility/Bundle.h>
|
||||
#include "Tactility/app/AppRegistration.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// Forward declarations
|
||||
typedef struct _lv_obj_t lv_obj_t;
|
||||
|
||||
namespace tt::app {
|
||||
|
||||
class App;
|
||||
@@ -40,8 +34,6 @@ enum class Result {
|
||||
|
||||
class Location {
|
||||
|
||||
private:
|
||||
|
||||
std::string path;
|
||||
Location() = default;
|
||||
explicit Location(const std::string& path) : path(path) {}
|
||||
@@ -54,7 +46,13 @@ public:
|
||||
return Location(path);
|
||||
}
|
||||
|
||||
/** Internal apps are all apps that are part of the firmware release. */
|
||||
bool isInternal() const { return path.empty(); }
|
||||
|
||||
/**
|
||||
* External apps are all apps that are not part of the firmware release.
|
||||
* e.g. an application on the sd card or one that is installed in /data
|
||||
*/
|
||||
bool isExternal() const { return !path.empty(); }
|
||||
const std::string& getPath() const { return path; }
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace tt::app::notes {
|
||||
|
||||
/**
|
||||
* Start the notes app with the specified text file.
|
||||
* @param[in] filePath the path to the text file to open
|
||||
*/
|
||||
void start(const std::string& filePath);
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace tt::app::textviewer {
|
||||
|
||||
void start(const std::string& file);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/Lock.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Some file systems belong to devices on a shared bus (e.g. SPI SD card).
|
||||
* Because of the shared bus, a lock is required for its operation.
|
||||
*/
|
||||
namespace tt::file {
|
||||
|
||||
/**
|
||||
* @param[in] path the path to find a lock for
|
||||
* @return a non-null lock for the specified path.
|
||||
*/
|
||||
std::shared_ptr<Lock> getLock(const std::string& path);
|
||||
|
||||
/**
|
||||
* Acquires a lock, calls the function, then releases the lock.
|
||||
* @param[in] path the path to find a lock for
|
||||
* @param[in] fn the code to execute while the lock is acquired
|
||||
*/
|
||||
template<typename ReturnType>
|
||||
ReturnType withLock(const std::string& path, std::function<ReturnType()> fn) {
|
||||
const auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return fn();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/file/File.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "FileLock.h"
|
||||
|
||||
/**
|
||||
* @warning The functionality below does NOT safely acquire file locks. Use file::getLock() or file::withLock() when using the functionality below.
|
||||
*/
|
||||
namespace tt::file {
|
||||
|
||||
class ObjectFileReader {
|
||||
|
||||
const std::string filePath;
|
||||
const uint32_t recordSize = 0;
|
||||
|
||||
std::unique_ptr<FILE, FileCloser> file;
|
||||
uint32_t recordCount = 0;
|
||||
uint32_t recordVersion = 0;
|
||||
uint32_t recordsRead = 0;
|
||||
|
||||
public:
|
||||
|
||||
ObjectFileReader(std::string filePath, uint32_t recordSize) :
|
||||
filePath(std::move(filePath)),
|
||||
recordSize(recordSize)
|
||||
{}
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool hasNext() const { return recordsRead < recordCount; }
|
||||
bool readNext(void* output);
|
||||
|
||||
uint32_t getRecordCount() const { return recordCount; }
|
||||
uint32_t getRecordSize() const { return recordSize; }
|
||||
uint32_t getRecordVersion() const { return recordVersion; }
|
||||
};
|
||||
|
||||
class ObjectFileWriter {
|
||||
|
||||
const std::string filePath;
|
||||
const uint32_t recordSize;
|
||||
const uint32_t recordVersion;
|
||||
const bool append;
|
||||
const std::shared_ptr<Lock> lock;
|
||||
|
||||
std::unique_ptr<FILE, FileCloser> file;
|
||||
uint32_t recordsWritten = 0;
|
||||
|
||||
public:
|
||||
|
||||
ObjectFileWriter(std::string filePath, uint32_t recordSize, uint32_t recordVersion, bool append) :
|
||||
filePath(std::move(filePath)),
|
||||
recordSize(recordSize),
|
||||
recordVersion(recordVersion),
|
||||
append(append),
|
||||
lock(getLock(filePath))
|
||||
{}
|
||||
|
||||
|
||||
~ObjectFileWriter() {
|
||||
if (file != nullptr) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
bool open();
|
||||
void close();
|
||||
|
||||
bool write(void* data);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @note The functionality below safely acquires and releases any SD card device locks. Manual locking isn't needed.
|
||||
*/
|
||||
namespace tt::file {
|
||||
|
||||
/**
|
||||
* Load a properties file into a map
|
||||
* @param[in] filePath the file to load
|
||||
* @param[out] properties the resulting properties
|
||||
* @return true when the properties file was opened successfully
|
||||
*/
|
||||
bool loadPropertiesFile(const std::string& filePath, std::map<std::string, std::string>& properties);
|
||||
|
||||
/**
|
||||
* Load a properties file and report key-values to a function
|
||||
* @param[in] filePath the file to load
|
||||
* @param[in] callback the callback function that receives the key-values
|
||||
* @return true when the properties file was opened successfully
|
||||
*/
|
||||
bool loadPropertiesFile(const std::string& filePath, std::function<void(const std::string& key, const std::string& value)> callback);
|
||||
|
||||
/**
|
||||
* Save properties to a file
|
||||
* @param[in] filePath the file to save to
|
||||
* @param[in] properties the properties to save
|
||||
* @return true when the data was written to the file succesfully
|
||||
*/
|
||||
bool savePropertiesFile(const std::string& filePath, const std::map<std::string, std::string>& properties);
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
Mounted,
|
||||
Unmounted,
|
||||
Error,
|
||||
Unknown
|
||||
Timeout // Failed to retrieve state due to timeout
|
||||
};
|
||||
|
||||
enum class MountBehaviour {
|
||||
@@ -35,32 +35,28 @@ public:
|
||||
|
||||
virtual bool mount(const std::string& mountPath) = 0;
|
||||
virtual bool unmount() = 0;
|
||||
virtual State getState() const = 0;
|
||||
virtual State getState(TickType_t timeout = portMAX_DELAY) const = 0;
|
||||
/** Return empty string when not mounted or the mount path if mounted */
|
||||
virtual std::string getMountPath() const = 0;
|
||||
|
||||
virtual Lock& getLock() const = 0;
|
||||
/** Non-null lock */
|
||||
virtual std::shared_ptr<Lock> getLock() const = 0;
|
||||
|
||||
virtual MountBehaviour getMountBehaviour() const { return mountBehaviour; }
|
||||
bool isMounted() const { return getState() == State::Mounted; }
|
||||
|
||||
/** @return true if the SD card was mounted, returns false when it was not or when a timeout happened. */
|
||||
bool isMounted(TickType_t timeout = portMAX_DELAY) const { return getState(timeout) == State::Mounted; }
|
||||
};
|
||||
|
||||
/** Return the SdCard device if the path is within the SdCard mounted path (path std::string::starts_with() check)*/
|
||||
std::shared_ptr<SdCardDevice> _Nullable find(const std::string& path);
|
||||
|
||||
/**
|
||||
* Acquires an SD card lock if the path is an SD card path.
|
||||
* Always calls the function, but doesn't lock if the path is not an SD card path.
|
||||
* Attempt to find an SD card that the specified belongs to,
|
||||
* and returns its lock if the SD card is mounted. Otherwise it returns nullptr.
|
||||
* @param[in] a path on a file system (e.g. file, directory, etc.)
|
||||
* @return the lock of a mounted SD card or otherwise null
|
||||
*/
|
||||
template<typename ReturnType>
|
||||
ReturnType withSdCardLock(const std::string& path, std::function<ReturnType()> fn) {
|
||||
auto sdcard = find(path);
|
||||
if (sdcard != nullptr) {
|
||||
auto scoped_lockable = sdcard->getLock().asScopedLock();
|
||||
scoped_lockable.lock(portMAX_DELAY);
|
||||
}
|
||||
|
||||
return fn();
|
||||
}
|
||||
std::shared_ptr<Lock> findSdCardLock(const std::string& path);
|
||||
|
||||
} // namespace tt::hal
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
gpio_num_t spiPinWp,
|
||||
gpio_num_t spiPinInt,
|
||||
MountBehaviour mountBehaviourAtBoot,
|
||||
/** When custom lock is nullptr, use the SPI default one */
|
||||
/** When customLock is a nullptr, use the SPI default one */
|
||||
std::shared_ptr<Lock> _Nullable customLock = nullptr,
|
||||
std::vector<gpio_num_t> csPinWorkAround = std::vector<gpio_num_t>(),
|
||||
spi_host_device_t spiHost = SPI2_HOST,
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
gpio_num_t spiPinCd; // Card detect
|
||||
gpio_num_t spiPinWp; // Write-protect
|
||||
gpio_num_t spiPinInt; // Interrupt
|
||||
SdCardDevice::MountBehaviour mountBehaviourAtBoot;
|
||||
MountBehaviour mountBehaviourAtBoot;
|
||||
std::shared_ptr<Lock> _Nullable customLock;
|
||||
std::vector<gpio_num_t> csPinWorkAround;
|
||||
spi_host_device_t spiHost;
|
||||
@@ -74,22 +74,22 @@ public:
|
||||
config(std::move(config))
|
||||
{}
|
||||
|
||||
std::string getName() const final { return "SD Card"; }
|
||||
std::string getDescription() const final { return "SD card via SPI interface"; }
|
||||
std::string getName() const override { return "SD Card"; }
|
||||
std::string getDescription() const override { return "SD card via SPI interface"; }
|
||||
|
||||
bool mount(const std::string& mountPath) final;
|
||||
bool unmount() final;
|
||||
std::string getMountPath() const final { return mountPath; }
|
||||
bool mount(const std::string& mountPath) override;
|
||||
bool unmount() override;
|
||||
std::string getMountPath() const override { return mountPath; }
|
||||
|
||||
Lock& getLock() const final {
|
||||
if (config->customLock) {
|
||||
return *config->customLock;
|
||||
std::shared_ptr<Lock> getLock() const override {
|
||||
if (config->customLock != nullptr) {
|
||||
return config->customLock;
|
||||
} else {
|
||||
return *spi::getLock(config->spiHost);
|
||||
return spi::getLock(config->spiHost);
|
||||
}
|
||||
}
|
||||
|
||||
State getState() const override;
|
||||
State getState(TickType_t timeout) const override;
|
||||
|
||||
sdmmc_card_t* _Nullable getCard() { return card; }
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ enum class SystemEvent {
|
||||
|
||||
/** Value 0 mean "no subscription" */
|
||||
typedef uint32_t SystemEventSubscription;
|
||||
constexpr SystemEventSubscription NoSystemEventSubscription = 0U;
|
||||
|
||||
typedef std::function<void(SystemEvent)> OnSystemEvent;
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "./WifiGlobals.h"
|
||||
#include "./WifiSettings.h"
|
||||
|
||||
#include <Tactility/PubSub.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "WifiApSettings.h"
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_wifi.h"
|
||||
#include "WifiSettings.h"
|
||||
#else
|
||||
#include <cstdint>
|
||||
// From esp_wifi_types.h in ESP-IDF 5.2
|
||||
@@ -124,7 +121,7 @@ std::string getIp();
|
||||
* @param[in] ap
|
||||
* @param[in] remember whether to save the ap data to the settings upon successful connection
|
||||
*/
|
||||
void connect(const settings::WifiApSettings* ap, bool remember);
|
||||
void connect(const settings::WifiApSettings& ap, bool remember);
|
||||
|
||||
/** @brief Disconnect from the access point. Doesn't have any effect when not connected. */
|
||||
void disconnect();
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace tt::service::wifi::settings {
|
||||
|
||||
/**
|
||||
* This struct is stored as-is into NVS flash.
|
||||
*
|
||||
* The SSID and secret are increased by 1 byte to facilitate string null termination.
|
||||
* This makes it easier to use the char array as a string in various places.
|
||||
*/
|
||||
struct WifiApSettings {
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
bool autoConnect;
|
||||
int32_t channel;
|
||||
|
||||
WifiApSettings(
|
||||
std::string ssid,
|
||||
std::string password,
|
||||
bool autoConnect = true,
|
||||
int32_t channel = 0
|
||||
) : ssid(ssid), password(password), autoConnect(autoConnect), channel(channel) {}
|
||||
|
||||
WifiApSettings() : ssid(""), password(""), autoConnect(true), channel(0) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if settings exist for the provided SSID
|
||||
* @param[in] ssid the access point to look for
|
||||
* @return true if the settings exist
|
||||
*/
|
||||
bool contains(const std::string& ssid);
|
||||
|
||||
/**
|
||||
* Load the settings for the provided SSID
|
||||
* @param[in] ssid the access point to look for
|
||||
* @param[out] settings the output settings
|
||||
* @return true if the settings were loaded successfully
|
||||
*/
|
||||
bool load(const std::string& ssid, WifiApSettings& settings);
|
||||
|
||||
/**
|
||||
* Save settings
|
||||
* @param settings the settings to save
|
||||
* @return true when the settings were saved successfully
|
||||
*/
|
||||
bool save(const WifiApSettings& settings);
|
||||
|
||||
/**
|
||||
* Remove settings that were saved previously.
|
||||
* @param ssid the name of the SSID for the settings to remove
|
||||
* @return true when settings were found and removed
|
||||
*/
|
||||
bool remove(const std::string& ssid);
|
||||
|
||||
} // namespace
|
||||
@@ -1,31 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "WifiGlobals.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace tt::service::wifi::settings {
|
||||
|
||||
/**
|
||||
* This struct is stored as-is into NVS flash.
|
||||
*
|
||||
* The SSID and secret are increased by 1 byte to facilitate string null termination.
|
||||
* This makes it easier to use the char array as a string in various places.
|
||||
*/
|
||||
struct WifiApSettings {
|
||||
char ssid[TT_WIFI_SSID_LIMIT + 1] = { 0 };
|
||||
char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT + 1] = { 0 };
|
||||
int32_t channel = 0;
|
||||
bool auto_connect = true;
|
||||
};
|
||||
|
||||
bool contains(const char* ssid);
|
||||
|
||||
bool load(const char* ssid, WifiApSettings* settings);
|
||||
|
||||
bool save(const WifiApSettings* settings);
|
||||
|
||||
bool remove(const char* ssid);
|
||||
|
||||
void setEnableOnBoot(bool enable);
|
||||
|
||||
bool shouldEnableOnBoot();
|
||||
|
||||
Reference in New Issue
Block a user