TactilityFreertos and more

This commit is contained in:
Ken Van Hoeylandt 2026-01-02 19:56:04 +01:00
parent 3fc2ff8bc6
commit bfe0158a4a
145 changed files with 1781 additions and 2622 deletions

View File

@ -21,5 +21,7 @@ jobs:
run: cmake --build build --target build-tests run: cmake --build build --target build-tests
- name: "Run TactilityCore Tests" - name: "Run TactilityCore Tests"
run: build/Tests/TactilityCore/TactilityCoreTests --exit run: build/Tests/TactilityCore/TactilityCoreTests --exit
- name: "Run TactilityFreertos Tests"
run: build/Tests/TactilityFreertos/TactilityFreertosTests --exit
- name: "Run TactilityHeadless Tests" - name: "Run TactilityHeadless Tests"
run: build/Tests/Tactility/TactilityTests --exit run: build/Tests/Tactility/TactilityTests --exit

View File

@ -37,6 +37,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
"Tactility" "Tactility"
"TactilityC" "TactilityC"
"TactilityCore" "TactilityCore"
"TactilityFreeRtos"
"Libraries/esp_lvgl_port" "Libraries/esp_lvgl_port"
"Libraries/elf_loader" "Libraries/elf_loader"
"Libraries/lvgl" "Libraries/lvgl"
@ -74,6 +75,7 @@ project(Tactility)
if (NOT DEFINED ENV{ESP_IDF_VERSION}) if (NOT DEFINED ENV{ESP_IDF_VERSION})
add_subdirectory(Tactility) add_subdirectory(Tactility)
add_subdirectory(TactilityCore) add_subdirectory(TactilityCore)
add_subdirectory(TactilityFreeRtos)
add_subdirectory(Devices/simulator) add_subdirectory(Devices/simulator)
endif () endif ()

View File

@ -1,13 +1,14 @@
#include "LvglTask.h" #include "LvglTask.h"
#include <Tactility/Log.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/Thread.h> #include <Tactility/Thread.h>
#include <Tactility/Log.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/Check.h>
#include <lvgl.h> #include <lvgl.h>
#define TAG "lvgl_task" constexpr auto TAG = "lvgl_task";
// Mutex for LVGL drawing // Mutex for LVGL drawing
static tt::RecursiveMutex lvgl_mutex; static tt::RecursiveMutex lvgl_mutex;

View File

@ -1,6 +1,6 @@
#include "Main.h" #include "Main.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/Thread.h> #include <Tactility/Thread.h>
#include <Tactility/TactilityCore.h>
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.h" #include "task.h"

View File

@ -19,6 +19,7 @@ else ()
target_link_libraries(FirmwareSim target_link_libraries(FirmwareSim
PRIVATE Tactility PRIVATE Tactility
PRIVATE TactilityCore PRIVATE TactilityCore
PRIVATE TactilityFreeRtos
PRIVATE Simulator PRIVATE Simulator
PRIVATE SDL2::SDL2-static SDL2-static PRIVATE SDL2::SDL2-static SDL2-static
) )

View File

@ -1,15 +1,20 @@
# Tactility # Tactility: Main License
The Tactility project is available under the [GNU General Public License v3](Documentation/license-tactility.md). The Tactility project code is available under the [GNU General Public License v3](Documentation/license-tactility.md).
Distributions and forks must adhere to the license terms. Distributions and forks must adhere to the license terms.
The Tactility logo copyrights are owned by Ken Van Hoeylandt. 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. 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). For other usages, [contact me](https://kenvanhoeylandt.net).
# TactilityC & TactilitySDK Third party projects that were included in Tactility retain their licenses.
TactilityC (source & binaries) and the TactilitySDK distribution files are available via the [Apache License Version 2.0](Documentation/license-tactilitysdk.md). # Tactility: Secondary License
The following projects are also available under [Apache License Version 2.0](Documentation/license-tactilitysdk.md):
- TactilityC
- TactilityFreertos
- TactilitySDK (source and binaries)
# Other licenses & copyrights # Other licenses & copyrights

View File

@ -8,6 +8,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
list(APPEND REQUIRES_LIST list(APPEND REQUIRES_LIST
TactilityCore TactilityCore
TactilityFreeRtos
lvgl lvgl
driver driver
elf_loader elf_loader
@ -71,6 +72,7 @@ else()
target_link_libraries(Tactility target_link_libraries(Tactility
PUBLIC cJSON PUBLIC cJSON
PUBLIC TactilityFreeRtos
PUBLIC TactilityCore PUBLIC TactilityCore
PUBLIC freertos_kernel PUBLIC freertos_kernel
PUBLIC lvgl PUBLIC lvgl

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Tactility/app/AppManifest.h>
#include <Tactility/Dispatcher.h> #include <Tactility/Dispatcher.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/hal/Configuration.h> #include <Tactility/hal/Configuration.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>

View File

@ -4,8 +4,8 @@
#include "GpsConfiguration.h" #include "GpsConfiguration.h"
#include "Satellites.h" #include "Satellites.h"
#include <Tactility/RecursiveMutex.h>
#include <Tactility/Thread.h> #include <Tactility/Thread.h>
#include <Tactility/RecursiveMutex.h>
#include <minmea.h> #include <minmea.h>
#include <utility> #include <utility>

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <Tactility/RtosCompat.h> #include <Tactility/freertoscompat/RTOS.h>
#include <Tactility/RecursiveMutex.h> #include <Tactility/RecursiveMutex.h>
#include <minmea.h> #include <minmea.h>

View File

@ -3,7 +3,7 @@
#include "./I2cCompat.h" #include "./I2cCompat.h"
#include "Tactility/Lock.h" #include "Tactility/Lock.h"
#include <Tactility/RtosCompat.h> #include <Tactility/freertoscompat/RTOS.h>
#include <climits> #include <climits>
#include <string> #include <string>

View File

@ -3,6 +3,7 @@
#include "../Device.h" #include "../Device.h"
#include <Tactility/TactilityCore.h> #include <Tactility/TactilityCore.h>
#include <Tactility/Lock.h>
namespace tt::hal::sdcard { namespace tt::hal::sdcard {
@ -51,7 +52,7 @@ public:
*/ */
virtual bool unmount() = 0; virtual bool unmount() = 0;
virtual State getState(TickType_t timeout = portMAX_DELAY) const = 0; virtual State getState(TickType_t timeout = kernel::MAX_TICKS) const = 0;
/** @return empty string when not mounted or the mount path if mounted */ /** @return empty string when not mounted or the mount path if mounted */
virtual std::string getMountPath() const = 0; virtual std::string getMountPath() const = 0;
@ -63,7 +64,7 @@ public:
virtual MountBehaviour getMountBehaviour() const { return mountBehaviour; } virtual MountBehaviour getMountBehaviour() const { return mountBehaviour; }
/** @return true if the SD card was mounted, returns false when it was not or when a timeout happened. */ /** @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; } bool isMounted(TickType_t timeout = kernel::MAX_TICKS) 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)*/ /** Return the SdCard device if the path is within the SdCard mounted path (path std::string::starts_with() check)*/

View File

@ -3,7 +3,7 @@
#include "SpiCompat.h" #include "SpiCompat.h"
#include <Tactility/Lock.h> #include <Tactility/Lock.h>
#include <Tactility/RtosCompat.h> #include <Tactility/freertoscompat/RTOS.h>
namespace tt::hal::spi { namespace tt::hal::spi {

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <Tactility/RtosCompat.h> #include <Tactility/freertoscompat/RTOS.h>
#include "../gpio/Gpio.h" #include "../gpio/Gpio.h"
#include "Tactility/Lock.h" #include "Tactility/Lock.h"

View File

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "Tactility/Mutex.h" #include <Tactility/PubSub.h>
#include "Tactility/RecursiveMutex.h" #include <Tactility/Mutex.h>
#include "Tactility/PubSub.h" #include <Tactility/RecursiveMutex.h>
#include "Tactility/hal/gps/GpsDevice.h" #include <Tactility/hal/gps/GpsDevice.h>
#include "Tactility/service/Service.h" #include <Tactility/service/Service.h>
#include "Tactility/service/ServiceContext.h" #include <Tactility/service/ServiceContext.h>
#include "Tactility/service/gps/GpsState.h" #include <Tactility/service/gps/GpsState.h>
namespace tt::service::gps { namespace tt::service::gps {

View File

@ -1,11 +1,11 @@
#pragma once #pragma once
#include <Tactility/app/AppInstance.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/Bundle.h>
#include <Tactility/DispatcherThread.h> #include <Tactility/DispatcherThread.h>
#include <Tactility/Bundle.h>
#include <Tactility/PubSub.h> #include <Tactility/PubSub.h>
#include <Tactility/RecursiveMutex.h> #include <Tactility/RecursiveMutex.h>
#include <Tactility/app/AppInstance.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/service/Service.h> #include <Tactility/service/Service.h>
#include <memory> #include <memory>

View File

@ -2,6 +2,7 @@
#include <Tactility/PubSub.h> #include <Tactility/PubSub.h>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>

View File

@ -5,6 +5,8 @@
#include <Tactility/app/ElfApp.h> #include <Tactility/app/ElfApp.h>
#include <Tactility/Bundle.h> #include <Tactility/Bundle.h>
#include <Tactility/Check.h>
#include <Tactility/Log.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <memory> #include <memory>

View File

@ -4,7 +4,7 @@
namespace tt::app::i2cscanner { namespace tt::app::i2cscanner {
#define TAG "i2cscanner" static constexpr auto TAG = "i2cscanner";
enum ScanState { enum ScanState {
ScanStateInitial, ScanStateInitial,

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <Tactility/app/AppContext.h>
#include <Tactility/MessageQueue.h> #include <Tactility/MessageQueue.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/PubSub.h> #include <Tactility/PubSub.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/app/AppContext.h>
#include <Tactility/service/Service.h> #include <Tactility/service/Service.h>
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>
@ -21,6 +21,7 @@ class GuiService final : public Service {
// Thread and lock // Thread and lock
Thread* thread = nullptr; Thread* thread = nullptr;
EventGroup threadFlags;
RecursiveMutex mutex; RecursiveMutex mutex;
PubSub<loader::LoaderService::Event>::SubscriptionHandle loader_pubsub_subscription = nullptr; PubSub<loader::LoaderService::Event>::SubscriptionHandle loader_pubsub_subscription = nullptr;
@ -49,7 +50,7 @@ class GuiService final : public Service {
} }
void unlock() const { void unlock() const {
tt_check(mutex.unlock()); mutex.unlock();
} }
void showApp(std::shared_ptr<app::AppInstance> app); void showApp(std::shared_ptr<app::AppInstance> app);

View File

@ -2,8 +2,8 @@
#include "Tactility/service/Service.h" #include "Tactility/service/Service.h"
#include <Tactility/Mutex.h>
#include <Tactility/Timer.h> #include <Tactility/Timer.h>
#include <Tactility/Mutex.h>
namespace tt::service::memorychecker { namespace tt::service::memorychecker {
@ -14,7 +14,7 @@ namespace tt::service::memorychecker {
class MemoryCheckerService final : public Service { class MemoryCheckerService final : public Service {
Mutex mutex; Mutex mutex;
Timer timer = Timer(Timer::Type::Periodic, [this] { onTimerUpdate(); }); Timer timer = Timer(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [this] { onTimerUpdate(); });
// LVGL Statusbar icon // LVGL Statusbar icon
int8_t statusbarIconId = -1; int8_t statusbarIconId = -1;

View File

@ -1,4 +1,4 @@
#include "Tactility/TactilityConfig.h" #include <Tactility/TactilityConfig.h>
#if TT_FEATURE_SCREENSHOT_ENABLED #if TT_FEATURE_SCREENSHOT_ENABLED

View File

@ -5,15 +5,15 @@
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/TactilityConfig.h> #include <Tactility/TactilityConfig.h>
#include <Tactility/DispatcherThread.h>
#include <Tactility/MountPoints.h>
#include <Tactility/app/AppManifestParsing.h> #include <Tactility/app/AppManifestParsing.h>
#include <Tactility/app/AppRegistration.h> #include <Tactility/app/AppRegistration.h>
#include <Tactility/DispatcherThread.h>
#include <Tactility/file/File.h> #include <Tactility/file/File.h>
#include <Tactility/file/FileLock.h> #include <Tactility/file/FileLock.h>
#include <Tactility/file/PropertiesFile.h> #include <Tactility/file/PropertiesFile.h>
#include <Tactility/hal/HalPrivate.h> #include <Tactility/hal/HalPrivate.h>
#include <Tactility/lvgl/LvglPrivate.h> #include <Tactility/lvgl/LvglPrivate.h>
#include <Tactility/MountPoints.h>
#include <Tactility/network/NtpPrivate.h> #include <Tactility/network/NtpPrivate.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>
#include <Tactility/service/ServiceRegistration.h> #include <Tactility/service/ServiceRegistration.h>

View File

@ -1,5 +1,6 @@
#include <Tactility/app/AppManifestParsing.h> #include <Tactility/app/AppManifestParsing.h>
#include <Tactility/Log.h>
#include <regex> #include <regex>
namespace tt::app { namespace tt::app {

View File

@ -1,5 +1,7 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include <Tactility/Timer.h>
#include <Tactility/Tactility.h>
#include <Tactility/app/AppManifest.h> #include <Tactility/app/AppManifest.h>
#include <Tactility/lvgl/Lvgl.h> #include <Tactility/lvgl/Lvgl.h>
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/lvgl/LvglSync.h>
@ -9,8 +11,6 @@
#include <Tactility/service/development/DevelopmentSettings.h> #include <Tactility/service/development/DevelopmentSettings.h>
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>
#include <Tactility/service/wifi/Wifi.h> #include <Tactility/service/wifi/Wifi.h>
#include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
#include <cstring> #include <cstring>
#include <lvgl.h> #include <lvgl.h>
@ -27,7 +27,7 @@ class DevelopmentApp final : public App {
lv_obj_t* statusLabel = nullptr; lv_obj_t* statusLabel = nullptr;
std::shared_ptr<service::development::DevelopmentService> service; std::shared_ptr<service::development::DevelopmentService> service;
Timer timer = Timer(Timer::Type::Periodic, [this] { Timer timer = Timer(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [this] {
auto lock = lvgl::getSyncLock()->asScopedLock(); auto lock = lvgl::getSyncLock()->asScopedLock();
// TODO: There's a crash when this is called when the app is being destroyed // TODO: There's a crash when this is called when the app is being destroyed
if (lock.lock(lvgl::defaultLockTime) && lvgl::isStarted()) { if (lock.lock(lvgl::defaultLockTime) && lvgl::isStarted()) {
@ -148,7 +148,7 @@ public:
updateViewState(); updateViewState();
timer.start(1000); timer.start();
} }
void onHide(AppContext& appContext) override { void onHide(AppContext& appContext) override {

View File

@ -5,7 +5,7 @@
#include <Tactility/hal/sdcard/SdCardDevice.h> #include <Tactility/hal/sdcard/SdCardDevice.h>
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/MountPoints.h> #include <Tactility/MountPoints.h>
#include <Tactility/kernel/Kernel.h> #include <Tactility/kernel/Platform.h>
#include <cstring> #include <cstring>
#include <unistd.h> #include <unistd.h>

View File

@ -1,22 +1,22 @@
#include <Tactility/app/files/View.h> #include <Tactility/app/files/View.h>
#include <Tactility/app/files/SupportedFiles.h> #include <Tactility/app/files/SupportedFiles.h>
#include <Tactility/file/File.h>
#include <Tactility/file/FileLock.h>
#include <Tactility/app/alertdialog/AlertDialog.h> #include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/app/imageviewer/ImageViewer.h> #include <Tactility/app/imageviewer/ImageViewer.h>
#include <Tactility/app/inputdialog/InputDialog.h> #include <Tactility/app/inputdialog/InputDialog.h>
#include <Tactility/app/notes/Notes.h> #include <Tactility/app/notes/Notes.h>
#include <Tactility/app/ElfApp.h> #include <Tactility/app/ElfApp.h>
#include <Tactility/kernel/Platform.h>
#include <Tactility/Log.h>
#include <Tactility/lvgl/Toolbar.h> #include <Tactility/lvgl/Toolbar.h>
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/lvgl/LvglSync.h>
#include <Tactility/Tactility.h>
#include <Tactility/file/File.h>
#include <Tactility/Log.h>
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
#include <Tactility/Tactility.h>
#include <cstring> #include <cstring>
#include <unistd.h> #include <unistd.h>
#include <Tactility/file/FileLock.h>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>

View File

@ -4,7 +4,7 @@
#include "Tactility/hal/sdcard/SdCardDevice.h" #include "Tactility/hal/sdcard/SdCardDevice.h"
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/MountPoints.h> #include <Tactility/MountPoints.h>
#include <Tactility/kernel/Kernel.h> #include <Tactility/kernel/Platform.h>
#include <cstring> #include <cstring>
#include <unistd.h> #include <unistd.h>

View File

@ -1,18 +1,19 @@
#include "Tactility/app/fileselection/View.h" #include <Tactility/app/fileselection/View.h>
#include "Tactility/app/alertdialog/AlertDialog.h" #include <Tactility/app/alertdialog/AlertDialog.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include "Tactility/file/File.h" #include <Tactility/file/File.h>
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
#include <Tactility/kernel/Platform.h>
#include <cstring> #include <cstring>
#include <unistd.h> #include <unistd.h>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#endif #endif
namespace tt::app::fileselection { namespace tt::app::fileselection {

View File

@ -1,5 +1,6 @@
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
#include <Tactility/app/AppManifest.h> #include <Tactility/app/AppManifest.h>
#include <Tactility/app/alertdialog/AlertDialog.h> #include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/lvgl/LvglSync.h>
@ -7,7 +8,6 @@
#include <Tactility/service/gps/GpsService.h> #include <Tactility/service/gps/GpsService.h>
#include <Tactility/service/gps/GpsState.h> #include <Tactility/service/gps/GpsState.h>
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>
#include <Tactility/Timer.h>
#include <cstring> #include <cstring>
#include <format> #include <format>
@ -62,7 +62,7 @@ class GpsSettingsApp final : public App {
} }
void startReceivingUpdates() { void startReceivingUpdates() {
timer->start(kernel::secondsToTicks(1)); timer->start();
updateViews(); updateViews();
} }
@ -268,7 +268,7 @@ class GpsSettingsApp final : public App {
public: public:
GpsSettingsApp() { GpsSettingsApp() {
timer = std::make_unique<Timer>(Timer::Type::Periodic, [this] { timer = std::make_unique<Timer>(Timer::Type::Periodic, kernel::secondsToTicks(1), [this] {
updateViews(); updateViews();
}); });
service = service::gps::findGpsService(); service = service::gps::findGpsService();

View File

@ -8,10 +8,10 @@
#include <Tactility/lvgl/Toolbar.h> #include <Tactility/lvgl/Toolbar.h>
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>
#include <Tactility/Timer.h>
#include <Tactility/Assets.h> #include <Tactility/Assets.h>
#include <Tactility/RecursiveMutex.h> #include <Tactility/RecursiveMutex.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
#include <format> #include <format>
@ -278,10 +278,10 @@ void I2cScannerApp::startScanning() {
lv_obj_clean(scanListWidget); lv_obj_clean(scanListWidget);
scanState = ScanStateScanning; scanState = ScanStateScanning;
scanTimer = std::make_unique<Timer>(Timer::Type::Once, [this]{ scanTimer = std::make_unique<Timer>(Timer::Type::Once, 10, [this]{
onScanTimer(); onScanTimer();
}); });
scanTimer->start(10); scanTimer->start();
mutex.unlock(); mutex.unlock();
} else { } else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning"); TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "startScanning");

View File

@ -1,12 +1,12 @@
#include "Tactility/app/AppContext.h" #include <Tactility/app/AppContext.h>
#include "Tactility/lvgl/LvglSync.h" #include <Tactility/lvgl/LvglSync.h>
#include "Tactility/lvgl/Style.h" #include <Tactility/lvgl/Style.h>
#include "Tactility/lvgl/Toolbar.h" #include <Tactility/lvgl/Toolbar.h>
#include "Tactility/service/loader/Loader.h" #include <Tactility/service/loader/Loader.h>
#include "Tactility/hal/power/PowerDevice.h" #include <Tactility/hal/power/PowerDevice.h>
#include <Tactility/Assets.h>
#include <Tactility/Timer.h> #include <Tactility/Timer.h>
#include <Tactility/Assets.h>
#include <Tactility/hal/Device.h> #include <Tactility/hal/Device.h>
#include <lvgl.h> #include <lvgl.h>
@ -31,7 +31,7 @@ std::shared_ptr<PowerApp> _Nullable optApp() {
class PowerApp : public App { class PowerApp : public App {
Timer update_timer = Timer(Timer::Type::Periodic, []() { onTimer(); }); Timer update_timer = Timer(Timer::Type::Periodic, kernel::millisToTicks(1000),[]() { onTimer(); });
std::shared_ptr<hal::power::PowerDevice> power; std::shared_ptr<hal::power::PowerDevice> power;
@ -180,7 +180,7 @@ public:
updateUi(); updateUi();
update_timer.start(kernel::millisToTicks(1000)); update_timer.start();
} }
void onHide(TT_UNUSED AppContext& app) override { void onHide(TT_UNUSED AppContext& app) override {

View File

@ -8,6 +8,7 @@
#include <Tactility/app/App.h> #include <Tactility/app/App.h>
#include <Tactility/app/AppManifest.h> #include <Tactility/app/AppManifest.h>
#include <Tactility/kernel/Platform.h>
#include <Tactility/lvgl/Lvgl.h> #include <Tactility/lvgl/Lvgl.h>
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/lvgl/LvglSync.h>
#include <Tactility/lvgl/Toolbar.h> #include <Tactility/lvgl/Toolbar.h>
@ -71,7 +72,7 @@ static void onModeSetCallback(TT_UNUSED lv_event_t* event) {
} }
ScreenshotApp::ScreenshotApp() { ScreenshotApp::ScreenshotApp() {
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [this]() { updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, 500 / portTICK_PERIOD_MS, [this] {
onTimerTick(); onTimerTick();
}); });
} }
@ -274,7 +275,7 @@ void ScreenshotApp::onShow(AppContext& appContext, lv_obj_t* parent) {
updateScreenshotMode(); updateScreenshotMode();
if (!updateTimer->isRunning()) { if (!updateTimer->isRunning()) {
updateTimer->start(500 / portTICK_PERIOD_MS); updateTimer->start();
} }
} }

View File

@ -7,9 +7,9 @@
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/loader/Loader.h> #include <Tactility/service/loader/Loader.h>
#include <Tactility/Timer.h>
#include <Tactility/MountPoints.h> #include <Tactility/MountPoints.h>
#include <Tactility/StringUtils.h> #include <Tactility/StringUtils.h>
#include <Tactility/Timer.h>
#include <lvgl.h> #include <lvgl.h>
#include <memory> #include <memory>
@ -82,7 +82,7 @@ class TimeZoneApp final : public App {
updateTimer->stop(); updateTimer->stop();
} }
updateTimer->start(500 / portTICK_PERIOD_MS); updateTimer->start();
mutex.unlock(); mutex.unlock();
} }
@ -220,7 +220,7 @@ public:
} }
void onCreate(AppContext& app) override { void onCreate(AppContext& app) override {
updateTimer = std::make_unique<Timer>(Timer::Type::Once, [this] { updateTimer = std::make_unique<Timer>(Timer::Type::Once, 500 / portTICK_PERIOD_MS, [this] {
updateList(); updateList();
}); });
} }

View File

@ -1,5 +1,6 @@
#include "Tactility/hal/Device.h" #include <Tactility/hal/Device.h>
#include <Tactility/Log.h>
#include <Tactility/RecursiveMutex.h> #include <Tactility/RecursiveMutex.h>
#include <algorithm> #include <algorithm>
@ -9,7 +10,7 @@ std::vector<std::shared_ptr<Device>> devices;
RecursiveMutex mutex; RecursiveMutex mutex;
static Device::Id nextId = 0; static Device::Id nextId = 0;
#define TAG "devices" constexpr auto TAG = "devices";
Device::Device() : id(nextId++) {} Device::Device() : id(nextId++) {}

View File

@ -1,7 +1,9 @@
#include "Tactility/hal/gps/GpsDevice.h" #include <Tactility/hal/gps/GpsDevice.h>
#include "Tactility/hal/gps/GpsInit.h" #include <Tactility/hal/gps/GpsInit.h>
#include "Tactility/hal/gps/Probe.h" #include <Tactility/hal/gps/Probe.h>
#include "Tactility/hal/uart/Uart.h" #include <Tactility/hal/uart/Uart.h>
#include <Tactility/Log.h>
#include <cstring> #include <cstring>
#include <minmea.h> #include <minmea.h>

View File

@ -1,12 +1,16 @@
#include "Tactility/hal/gps/Cas.h" #include <Tactility/Check.h>
#include "Tactility/hal/gps/GpsDevice.h" #include <Tactility/hal/gps/Cas.h>
#include "Tactility/hal/gps/Ublox.h" #include <Tactility/hal/gps/GpsDevice.h>
#include <Tactility/hal/gps/Ublox.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Log.h>
#include <cstring> #include <cstring>
#define TAG "gps"
namespace tt::hal::gps { namespace tt::hal::gps {
constexpr auto TAG = "gps";
bool initMtk(uart::Uart& uart); bool initMtk(uart::Uart& uart);
bool initMtkL76b(uart::Uart& uart); bool initMtkL76b(uart::Uart& uart);
bool initMtkPa1616s(uart::Uart& uart); bool initMtkPa1616s(uart::Uart& uart);

View File

@ -1,12 +1,14 @@
#include "Tactility/hal/gps/Satellites.h" #include <Tactility/hal/gps/Satellites.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Log.h>
#include <algorithm> #include <algorithm>
#define TAG "satellites"
namespace tt::hal::gps { namespace tt::hal::gps {
constexpr inline bool hasTimeElapsed(TickType_t now, TickType_t timeInThePast, TickType_t expireTimeInTicks) { constexpr auto TAG = "Satellites";
constexpr bool hasTimeElapsed(TickType_t now, TickType_t timeInThePast, TickType_t expireTimeInTicks) {
return (TickType_t)(now - timeInThePast) >= expireTimeInTicks; return (TickType_t)(now - timeInThePast) >= expireTimeInTicks;
} }
@ -45,7 +47,7 @@ SatelliteStorage::SatelliteRecord* SatelliteStorage::findRecordToRecycle() {
lock.lock(); lock.lock();
int candidate_index = -1; int candidate_index = -1;
auto candidate_age = portMAX_DELAY; auto candidate_age = kernel::MAX_TICKS;
TickType_t expire_duration = kernel::secondsToTicks(recycleTimeSeconds); TickType_t expire_duration = kernel::secondsToTicks(recycleTimeSeconds);
TickType_t now = kernel::getTicks(); TickType_t now = kernel::getTicks();
for (int i = 0; i < records.size(); ++i) { for (int i = 0; i < records.size(); ++i) {

View File

@ -1,12 +1,15 @@
#include "Tactility/hal/gps/Ublox.h" #include <Tactility/hal/gps/Ublox.h>
#include "Tactility/hal/gps/UbloxMessages.h" #include <Tactility/hal/gps/UbloxMessages.h>
#include "Tactility/hal/uart/Uart.h" #include <Tactility/hal/uart/Uart.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Log.h>
#include <cstring> #include <cstring>
#define TAG "ublox"
namespace tt::hal::gps::ublox { namespace tt::hal::gps::ublox {
constexpr auto TAG = "ublox";
bool initUblox6(uart::Uart& uart); bool initUblox6(uart::Uart& uart);
bool initUblox789(uart::Uart& uart, GpsModel model); bool initUblox789(uart::Uart& uart, GpsModel model);
bool initUblox10(uart::Uart& uart); bool initUblox10(uart::Uart& uart);

View File

@ -2,15 +2,12 @@
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <Tactility/Check.h>
#ifdef ESP_PLATFORM
#include <esp_check.h>
#endif // ESP_PLATFORM
#define TAG "i2c"
namespace tt::hal::i2c { namespace tt::hal::i2c {
constexpr auto TAG = "i2c";
struct Data { struct Data {
Mutex mutex; Mutex mutex;
bool isConfigured = false; bool isConfigured = false;

View File

@ -1,5 +1,6 @@
#include "Tactility/hal/spi/Spi.h" #include <Tactility/hal/spi/Spi.h>
#include <Tactility/Log.h>
#include <Tactility/RecursiveMutex.h> #include <Tactility/RecursiveMutex.h>
namespace tt::hal::spi { namespace tt::hal::spi {

View File

@ -1,17 +1,18 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include "Tactility/hal/uart/UartEsp.h" #include <Tactility/hal/uart/UartEsp.h>
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <sstream>
#include <esp_check.h> #include <esp_check.h>
#include <sstream>
#define TAG "uart"
namespace tt::hal::uart { namespace tt::hal::uart {
constexpr auto TAG = "uart";
bool UartEsp::start() { bool UartEsp::start() {
TT_LOG_I(TAG, "[%s] Starting", configuration.name.c_str()); TT_LOG_I(TAG, "[%s] Starting", configuration.name.c_str());

View File

@ -1,8 +1,8 @@
#ifndef ESP_PLATFORM #ifndef ESP_PLATFORM
#include "Tactility/hal/uart/UartPosix.h" #include <Tactility/hal/uart/UartPosix.h>
#include "Tactility/hal/uart/Uart.h" #include <Tactility/hal/uart/Uart.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <cstring> #include <cstring>
@ -10,10 +10,10 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#define TAG "uart"
namespace tt::hal::uart { namespace tt::hal::uart {
constexpr auto TAG = "uart";
bool UartPosix::start() { bool UartPosix::start() {
auto lock = mutex.asScopedLock(); auto lock = mutex.asScopedLock();
lock.lock(); lock.lock();

View File

@ -1,6 +1,9 @@
#include "Tactility/kernel/SystemEvents.h" #include <Tactility/kernel/SystemEvents.h>
#include <Tactility/Check.h>
#include <Tactility/Log.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <list> #include <list>
namespace tt::kernel { namespace tt::kernel {
@ -56,7 +59,7 @@ static const char* getEventName(SystemEvent event) {
void publishSystemEvent(SystemEvent event) { void publishSystemEvent(SystemEvent event) {
TT_LOG_I(TAG, "%s", getEventName(event)); TT_LOG_I(TAG, "%s", getEventName(event));
if (mutex.lock(portMAX_DELAY)) { if (mutex.lock(kernel::MAX_TICKS)) {
for (auto& subscription : subscriptions) { for (auto& subscription : subscriptions) {
if (subscription.event == event) { if (subscription.event == event) {
subscription.handler(event); subscription.handler(event);
@ -68,7 +71,7 @@ void publishSystemEvent(SystemEvent event) {
} }
SystemEventSubscription subscribeSystemEvent(SystemEvent event, OnSystemEvent handler) { SystemEventSubscription subscribeSystemEvent(SystemEvent event, OnSystemEvent handler) {
if (mutex.lock(portMAX_DELAY)) { if (mutex.lock(kernel::MAX_TICKS)) {
auto id = ++subscriptionCounter; auto id = ++subscriptionCounter;
subscriptions.push_back({ subscriptions.push_back({
@ -85,7 +88,7 @@ SystemEventSubscription subscribeSystemEvent(SystemEvent event, OnSystemEvent ha
} }
void unsubscribeSystemEvent(SystemEventSubscription subscription) { void unsubscribeSystemEvent(SystemEventSubscription subscription) {
if (mutex.lock(portMAX_DELAY)) { if (mutex.lock(kernel::MAX_TICKS)) {
std::erase_if(subscriptions, [subscription](auto& item) { std::erase_if(subscriptions, [subscription](auto& item) {
return (item.id == subscription); return (item.id == subscription);
}); });

View File

@ -1,9 +1,12 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/Thread.h>
#include <esp_lvgl_port.h>
#include <Tactility/CpuAffinity.h> #include <Tactility/CpuAffinity.h>
#include <Tactility/Log.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <Tactility/lvgl/LvglSync.h>
#include <esp_lvgl_port.h>
// LVGL // LVGL
// The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios // The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios

View File

@ -22,14 +22,14 @@ void syncSet(LvglLock lock, LvglUnlock unlock) {
auto old_unlock = unlock_singleton; auto old_unlock = unlock_singleton;
// Ensure the old lock is not engaged when changing locks // Ensure the old lock is not engaged when changing locks
old_lock((uint32_t)portMAX_DELAY); old_lock((uint32_t)kernel::MAX_TICKS);
lock_singleton = lock; lock_singleton = lock;
unlock_singleton = unlock; unlock_singleton = unlock;
old_unlock(); old_unlock();
} }
bool lock(TickType_t timeout) { bool lock(TickType_t timeout) {
return lock_singleton(pdMS_TO_TICKS(timeout == 0 ? portMAX_DELAY : timeout)); return lock_singleton(pdMS_TO_TICKS(timeout == 0 ? kernel::MAX_TICKS : timeout));
} }
void unlock() { void unlock() {
@ -44,9 +44,8 @@ public:
return lvgl::lock(timeoutTicks); return lvgl::lock(timeoutTicks);
} }
bool unlock() const override { void unlock() const override {
lvgl::unlock(); lvgl::unlock();
return true;
} }
}; };

View File

@ -3,14 +3,14 @@
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/TactilityCore.h> #include <Tactility/TactilityCore.h>
#include <Tactility/PubSub.h>
#include <Tactility/Timer.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/kernel/SystemEvents.h> #include <Tactility/kernel/SystemEvents.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/lvgl/Statusbar.h> #include <Tactility/lvgl/Statusbar.h>
#include <Tactility/lvgl/Style.h> #include <Tactility/lvgl/Style.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/PubSub.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/settings/Time.h> #include <Tactility/settings/Time.h>
#include <Tactility/Timer.h>
#include <lvgl.h> #include <lvgl.h>
@ -30,7 +30,7 @@ struct StatusbarData {
RecursiveMutex mutex; RecursiveMutex mutex;
std::shared_ptr<PubSub<void*>> pubsub = std::make_shared<PubSub<void*>>(); std::shared_ptr<PubSub<void*>> pubsub = std::make_shared<PubSub<void*>>();
StatusbarIcon icons[STATUSBAR_ICON_LIMIT] = {}; StatusbarIcon icons[STATUSBAR_ICON_LIMIT] = {};
Timer* time_update_timer = new Timer(Timer::Type::Once, [] { onUpdateTime(); }); Timer* time_update_timer = new Timer(Timer::Type::Once, 200 / portTICK_PERIOD_MS, [] { onUpdateTime(); });
uint8_t time_hours = 0; uint8_t time_hours = 0;
uint8_t time_minutes = 0; uint8_t time_minutes = 0;
bool time_set = false; bool time_set = false;
@ -73,12 +73,12 @@ static void onUpdateTime() {
statusbar_data.time_set = true; statusbar_data.time_set = true;
// Reschedule // Reschedule
statusbar_data.time_update_timer->start(getNextUpdateTime()); statusbar_data.time_update_timer->reset(getNextUpdateTime());
// Notify widget // Notify widget
statusbar_data.pubsub->publish(nullptr); statusbar_data.pubsub->publish(nullptr);
} else { } else {
statusbar_data.time_update_timer->start(pdMS_TO_TICKS(60000U)); statusbar_data.time_update_timer->reset(pdMS_TO_TICKS(60000U));
} }
statusbar_data.mutex.unlock(); statusbar_data.mutex.unlock();
@ -113,9 +113,7 @@ static void statusbar_pubsub_event(Statusbar* statusbar) {
static void onTimeChanged(TT_UNUSED kernel::SystemEvent event) { static void onTimeChanged(TT_UNUSED kernel::SystemEvent event) {
if (statusbar_data.mutex.lock()) { if (statusbar_data.mutex.lock()) {
statusbar_data.time_update_timer->stop(); statusbar_data.time_update_timer->reset(5);
statusbar_data.time_update_timer->start(5);
statusbar_data.mutex.unlock(); statusbar_data.mutex.unlock();
} }
} }
@ -131,7 +129,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
}); });
if (!statusbar_data.time_update_timer->isRunning()) { if (!statusbar_data.time_update_timer->isRunning()) {
statusbar_data.time_update_timer->start(200 / portTICK_PERIOD_MS); statusbar_data.time_update_timer->start();
statusbar_data.systemEventSubscription = kernel::subscribeSystemEvent( statusbar_data.systemEventSubscription = kernel::subscribeSystemEvent(
kernel::SystemEvent::Time, kernel::SystemEvent::Time,
onTimeChanged onTimeChanged
@ -177,7 +175,7 @@ lv_obj_t* statusbar_create(lv_obj_t* parent) {
obj_set_style_bg_invisible(left_spacer); obj_set_style_bg_invisible(left_spacer);
lv_obj_set_flex_grow(left_spacer, 1); lv_obj_set_flex_grow(left_spacer, 1);
statusbar_data.mutex.lock(portMAX_DELAY); statusbar_data.mutex.lock(kernel::MAX_TICKS);
for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) { for (int i = 0; i < STATUSBAR_ICON_LIMIT; ++i) {
auto* image = lv_image_create(obj); auto* image = lv_image_create(obj);
lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE); lv_obj_set_size(image, STATUSBAR_ICON_SIZE, STATUSBAR_ICON_SIZE);

View File

@ -31,6 +31,7 @@ void download(
auto certificate_length = strlen(reinterpret_cast<const char*>(certificate.get())) + 1; auto certificate_length = strlen(reinterpret_cast<const char*>(certificate.get())) + 1;
// TODO: Fix for missing initializer warnings
auto config = std::make_unique<esp_http_client_config_t>(esp_http_client_config_t { auto config = std::make_unique<esp_http_client_config_t>(esp_http_client_config_t {
.url = url.c_str(), .url = url.c_str(),
.auth_type = HTTP_AUTH_TYPE_NONE, .auth_type = HTTP_AUTH_TYPE_NONE,

View File

@ -1,6 +1,8 @@
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
#include <Tactility/network/HttpServer.h> #include <Tactility/network/HttpServer.h>
#include <Tactility/Log.h>
#include <Tactility/service/wifi/Wifi.h> #include <Tactility/service/wifi/Wifi.h>
namespace tt::network { namespace tt::network {

View File

@ -1,10 +1,10 @@
#include <Tactility/service/ServiceRegistration.h> #include <Tactility/service/ServiceRegistration.h>
#include <Tactility/Log.h>
#include <Tactility/Mutex.h>
#include <Tactility/service/ServiceInstance.h> #include <Tactility/service/ServiceInstance.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>
#include <Tactility/Mutex.h>
#include <string> #include <string>
namespace tt::service { namespace tt::service {

View File

@ -4,9 +4,10 @@
#ifdef CONFIG_ESP_WIFI_ENABLED #ifdef CONFIG_ESP_WIFI_ENABLED
#include "Tactility/service/espnow/EspNow.h" #include <Tactility/kernel/Kernel.h>
#include "Tactility/service/wifi/Wifi.h"
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/service/espnow/EspNow.h>
#include <Tactility/service/wifi/Wifi.h>
#include <esp_now.h> #include <esp_now.h>
#include <esp_wifi.h> #include <esp_wifi.h>

View File

@ -25,27 +25,30 @@ void GuiService::onLoaderEvent(LoaderService::Event event) {
} }
int32_t GuiService::guiMain() { int32_t GuiService::guiMain() {
auto service = findServiceById<GuiService>(manifest.id);
while (true) { while (true) {
uint32_t flags = Thread::awaitFlags(GUI_THREAD_FLAG_ALL, EventFlag::WaitAny, portMAX_DELAY); uint32_t flags = 0;
if (service->threadFlags.wait(GUI_THREAD_FLAG_ALL, false, true, portMAX_DELAY, &flags)) {
// When service not started or starting -> exit // When service not started or starting -> exit
State service_state = getState(manifest.id); State service_state = getState(manifest.id);
if (service_state != State::Started && service_state != State::Starting) { if (service_state != State::Started && service_state != State::Starting) {
break; break;
}
// Process and dispatch draw call
if (flags & GUI_THREAD_FLAG_DRAW) {
Thread::clearFlags(GUI_THREAD_FLAG_DRAW);
auto service = findService();
if (service != nullptr) {
service->redraw();
} }
}
if (flags & GUI_THREAD_FLAG_EXIT) { // Process and dispatch draw call
Thread::clearFlags(GUI_THREAD_FLAG_EXIT); if (flags & GUI_THREAD_FLAG_DRAW) {
break; service->threadFlags.clear(GUI_THREAD_FLAG_DRAW);
auto service = findService();
if (service != nullptr) {
service->redraw();
}
}
if (flags & GUI_THREAD_FLAG_EXIT) {
service->threadFlags.clear(GUI_THREAD_FLAG_EXIT);
break;
}
} }
} }
@ -177,8 +180,8 @@ void GuiService::onStop(TT_UNUSED ServiceContext& service) {
appToRender = nullptr; appToRender = nullptr;
isStarted = false; isStarted = false;
ThreadId thread_id = thread->getId(); auto task_handle = thread->getTaskHandle();
Thread::setFlags(thread_id, GUI_THREAD_FLAG_EXIT); threadFlags.set(GUI_THREAD_FLAG_EXIT);
thread->join(); thread->join();
delete thread; delete thread;
@ -190,8 +193,8 @@ void GuiService::onStop(TT_UNUSED ServiceContext& service) {
} }
void GuiService::requestDraw() { void GuiService::requestDraw() {
ThreadId thread_id = thread->getId(); auto task_handle = thread->getTaskHandle();
Thread::setFlags(thread_id, GUI_THREAD_FLAG_DRAW); threadFlags.set(GUI_THREAD_FLAG_DRAW);
} }
void GuiService::showApp(std::shared_ptr<app::AppInstance> app) { void GuiService::showApp(std::shared_ptr<app::AppInstance> app) {

View File

@ -8,7 +8,6 @@
namespace tt::service::memorychecker { namespace tt::service::memorychecker {
constexpr const char* TAG = "MemoryChecker"; constexpr const char* TAG = "MemoryChecker";
constexpr TickType_t TIMER_UPDATE_INTERVAL = 1000U / portTICK_PERIOD_MS;
// Total memory (in bytes) that should be free before warnings occur // Total memory (in bytes) that should be free before warnings occur
constexpr auto TOTAL_FREE_THRESHOLD = 10'000; constexpr auto TOTAL_FREE_THRESHOLD = 10'000;
@ -58,8 +57,8 @@ bool MemoryCheckerService::onStart(ServiceContext& service) {
statusbarIconId = lvgl::statusbar_icon_add(icon_path, false); statusbarIconId = lvgl::statusbar_icon_add(icon_path, false);
lvgl::statusbar_icon_set_visibility(statusbarIconId, false); lvgl::statusbar_icon_set_visibility(statusbarIconId, false);
timer.setThreadPriority(Thread::Priority::Lower); timer.setCallbackPriority(Thread::Priority::Lower);
timer.start(TIMER_UPDATE_INTERVAL); timer.start();
return true; return true;
} }

View File

@ -2,11 +2,13 @@
#if TT_FEATURE_SCREENSHOT_ENABLED #if TT_FEATURE_SCREENSHOT_ENABLED
#include <Tactility/Log.h>
#include <Tactility/LogMessages.h>
#include <Tactility/service/screenshot/Screenshot.h> #include <Tactility/service/screenshot/Screenshot.h>
#include <Tactility/service/ServiceRegistration.h> #include <Tactility/service/ServiceRegistration.h>
#include <memory>
#include <lvgl.h> #include <lvgl.h>
#include <memory>
namespace tt::service::screenshot { namespace tt::service::screenshot {

View File

@ -159,7 +159,7 @@ void ScreenshotTask::stop() {
if (thread != nullptr) { if (thread != nullptr) {
if (mutex.lock(50 / portTICK_PERIOD_MS)) { if (mutex.lock(50 / portTICK_PERIOD_MS)) {
interrupted = true; interrupted = true;
tt_check(mutex.unlock()); mutex.unlock();
} }
thread->join(); thread->join();
@ -167,7 +167,7 @@ void ScreenshotTask::stop() {
if (mutex.lock(50 / portTICK_PERIOD_MS)) { if (mutex.lock(50 / portTICK_PERIOD_MS)) {
delete thread; delete thread;
thread = nullptr; thread = nullptr;
tt_check(mutex.unlock()); mutex.unlock();
} }
} }
} }

View File

@ -1,9 +1,9 @@
#include <Tactility/service/ServiceContext.h> #include <Tactility/service/ServiceContext.h>
#include <Tactility/service/ServiceRegistration.h> #include <Tactility/service/ServiceRegistration.h>
#include <Tactility/Timer.h>
#include <Tactility/Mutex.h> #include <Tactility/Mutex.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
#include <Tactility/hal/sdcard/SdCardDevice.h> #include <Tactility/hal/sdcard/SdCardDevice.h>
namespace tt::service::sdcard { namespace tt::service::sdcard {
@ -60,12 +60,12 @@ public:
} }
auto service = findServiceById<SdCardService>(manifest.id); auto service = findServiceById<SdCardService>(manifest.id);
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service]() { updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, 1000, [service] {
service->update(); service->update();
}); });
// We want to try and scan more often in case of startup or scan lock failure // We want to try and scan more often in case of startup or scan lock failure
updateTimer->start(1000); updateTimer->start();
return true; return true;
} }

View File

@ -1,16 +1,16 @@
#include <Tactility/lvgl/Statusbar.h> #include <Tactility/lvgl/Statusbar.h>
#include <Tactility/Timer.h>
#include <Tactility/Mutex.h>
#include <Tactility/hal/power/PowerDevice.h> #include <Tactility/hal/power/PowerDevice.h>
#include <Tactility/hal/sdcard/SdCardDevice.h> #include <Tactility/hal/sdcard/SdCardDevice.h>
#include <Tactility/lvgl/Lvgl.h> #include <Tactility/lvgl/Lvgl.h>
#include <Tactility/lvgl/LvglSync.h> #include <Tactility/lvgl/LvglSync.h>
#include <Tactility/Mutex.h>
#include <Tactility/service/gps/GpsService.h>
#include <Tactility/service/ServiceContext.h> #include <Tactility/service/ServiceContext.h>
#include <Tactility/service/ServicePaths.h> #include <Tactility/service/ServicePaths.h>
#include <Tactility/service/ServiceRegistration.h> #include <Tactility/service/ServiceRegistration.h>
#include <Tactility/service/gps/GpsService.h>
#include <Tactility/service/wifi/Wifi.h> #include <Tactility/service/wifi/Wifi.h>
#include <Tactility/Timer.h>
namespace tt::service::statusbar { namespace tt::service::statusbar {
@ -265,14 +265,14 @@ public:
assert(service); assert(service);
service->update(); service->update();
updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, [service] { updateTimer = std::make_unique<Timer>(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [service] {
service->update(); service->update();
}); });
updateTimer->setThreadPriority(Thread::Priority::Lower); updateTimer->setCallbackPriority(Thread::Priority::Lower);
// We want to try and scan more often in case of startup or scan lock failure // We want to try and scan more often in case of startup or scan lock failure
updateTimer->start(1000); updateTimer->start();
return true; return true;
} }

View File

@ -1,5 +1,6 @@
#include "Tactility/service/wifi/Wifi.h" #include <Tactility/service/wifi/Wifi.h>
#include <Tactility/Check.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>
#include <Tactility/service/ServiceRegistration.h> #include <Tactility/service/ServiceRegistration.h>

View File

@ -6,15 +6,15 @@
#include <Tactility/service/wifi/Wifi.h> #include <Tactility/service/wifi/Wifi.h>
#include <Tactility/EventFlag.h> #include <Tactility/Timer.h>
#include <Tactility/EventGroup.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/Tactility.h> #include <Tactility/Tactility.h>
#include <Tactility/kernel/SystemEvents.h> #include <Tactility/kernel/SystemEvents.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/service/ServiceContext.h> #include <Tactility/service/ServiceContext.h>
#include <Tactility/service/wifi/WifiBootSplashInit.h>
#include <Tactility/service/wifi/WifiGlobals.h> #include <Tactility/service/wifi/WifiGlobals.h>
#include <Tactility/service/wifi/WifiSettings.h> #include <Tactility/service/wifi/WifiSettings.h>
#include <Tactility/service/wifi/WifiBootSplashInit.h>
#include <Tactility/Timer.h>
#include <lwip/esp_netif_net_stack.h> #include <lwip/esp_netif_net_stack.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
@ -66,10 +66,10 @@ public:
/** @brief Maximum amount of records to scan (value > 0) */ /** @brief Maximum amount of records to scan (value > 0) */
uint16_t scan_list_limit = TT_WIFI_SCAN_RECORD_LIMIT; uint16_t scan_list_limit = TT_WIFI_SCAN_RECORD_LIMIT;
/** @brief when we last requested a scan. Loops around every 50 days. */ /** @brief when we last requested a scan. Loops around every 50 days. */
TickType_t last_scan_time = portMAX_DELAY; TickType_t last_scan_time = kernel::MAX_TICKS;
esp_event_handler_instance_t event_handler_any_id = nullptr; esp_event_handler_instance_t event_handler_any_id = nullptr;
esp_event_handler_instance_t event_handler_got_ip = nullptr; esp_event_handler_instance_t event_handler_got_ip = nullptr;
EventFlag connection_wait_flags; EventGroup connection_wait_flags;
settings::WifiApSettings connection_target; settings::WifiApSettings connection_target;
bool pause_auto_connect = false; // Pause when manually disconnecting until manually connecting again bool pause_auto_connect = false; // Pause when manually disconnecting until manually connecting again
bool connection_target_remember = false; // Whether to store the connection_target on successful connection or not bool connection_target_remember = false; // Whether to store the connection_target on successful connection or not
@ -792,32 +792,34 @@ static void dispatchConnect(std::shared_ptr<Wifi> wifi) {
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) /* Waiting until either the connection is established (WIFI_CONNECTED_BIT)
* or connection failed for the maximum number of re-tries (WIFI_FAIL_BIT). * or connection failed for the maximum number of re-tries (WIFI_FAIL_BIT).
* The bits are set by wifi_event_handler() */ * The bits are set by wifi_event_handler() */
uint32_t bits = wifi_singleton->connection_wait_flags.wait(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); uint32_t bits;
TT_LOG_I(TAG, "Waiting for EventFlag by event_handler()"); if (wifi_singleton->connection_wait_flags.wait(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT, false, true, kernel::MAX_TICKS, &bits)) {
TT_LOG_I(TAG, "Waiting for EventGroup by event_handler()");
if (bits & WIFI_CONNECTED_BIT) { if (bits & WIFI_CONNECTED_BIT) {
wifi->setSecureConnection(config.sta.password[0] != 0x00U); wifi->setSecureConnection(config.sta.password[0] != 0x00U);
wifi->setRadioState(RadioState::ConnectionActive); wifi->setRadioState(RadioState::ConnectionActive);
publish_event(wifi, WifiEvent::ConnectionSuccess); publish_event(wifi, WifiEvent::ConnectionSuccess);
TT_LOG_I(TAG, "Connected to %s", wifi->connection_target.ssid.c_str()); TT_LOG_I(TAG, "Connected to %s", wifi->connection_target.ssid.c_str());
if (wifi->connection_target_remember) { if (wifi->connection_target_remember) {
if (!settings::save(wifi->connection_target)) { if (!settings::save(wifi->connection_target)) {
TT_LOG_E(TAG, "Failed to store credentials"); TT_LOG_E(TAG, "Failed to store credentials");
} else { } else {
TT_LOG_I(TAG, "Stored credentials"); TT_LOG_I(TAG, "Stored credentials");
}
} }
} else if (bits & WIFI_FAIL_BIT) {
wifi->setRadioState(RadioState::On);
publish_event(wifi, WifiEvent::ConnectionFailed);
TT_LOG_I(TAG, "Failed to connect to %s", wifi->connection_target.ssid.c_str());
} else {
wifi->setRadioState(RadioState::On);
publish_event(wifi, WifiEvent::ConnectionFailed);
TT_LOG_E(TAG, "UNEXPECTED EVENT");
} }
} else if (bits & WIFI_FAIL_BIT) {
wifi->setRadioState(RadioState::On);
publish_event(wifi, WifiEvent::ConnectionFailed);
TT_LOG_I(TAG, "Failed to connect to %s", wifi->connection_target.ssid.c_str());
} else {
wifi->setRadioState(RadioState::On);
publish_event(wifi, WifiEvent::ConnectionFailed);
TT_LOG_E(TAG, "UNEXPECTED EVENT");
}
wifi_singleton->connection_wait_flags.clear(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); wifi_singleton->connection_wait_flags.clear(WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
}
} }
static void dispatchDisconnectButKeepActive(std::shared_ptr<Wifi> wifi) { static void dispatchDisconnectButKeepActive(std::shared_ptr<Wifi> wifi) {
@ -920,9 +922,10 @@ public:
bootSplashInit(); bootSplashInit();
}); });
wifi_singleton->autoConnectTimer = std::make_unique<Timer>(Timer::Type::Periodic, []() { onAutoConnectTimer(); }); auto timer_interval = std::min(2000, AUTO_SCAN_INTERVAL);
wifi_singleton->autoConnectTimer = std::make_unique<Timer>(Timer::Type::Periodic, timer_interval, [] { onAutoConnectTimer(); });
// We want to try and scan more often in case of startup or scan lock failure // We want to try and scan more often in case of startup or scan lock failure
wifi_singleton->autoConnectTimer->start(std::min(2000, AUTO_SCAN_INTERVAL)); wifi_singleton->autoConnectTimer->start();
if (settings::shouldEnableOnBoot()) { if (settings::shouldEnableOnBoot()) {
TT_LOG_I(TAG, "Auto-enabling due to setting"); TT_LOG_I(TAG, "Auto-enabling due to setting");

View File

@ -2,9 +2,9 @@
#include <Tactility/service/wifi/Wifi.h> #include <Tactility/service/wifi/Wifi.h>
#include <Tactility/PubSub.h>
#include <Tactility/Check.h> #include <Tactility/Check.h>
#include <Tactility/Log.h> #include <Tactility/Log.h>
#include <Tactility/PubSub.h>
#include <Tactility/RecursiveMutex.h> #include <Tactility/RecursiveMutex.h>
#include <Tactility/service/Service.h> #include <Tactility/service/Service.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>
@ -81,26 +81,31 @@ std::vector<ApRecord> getScanResults() {
records.push_back((ApRecord) { records.push_back((ApRecord) {
.ssid = "Home Wifi", .ssid = "Home Wifi",
.rssi = -30, .rssi = -30,
.channel = 0,
.auth_mode = WIFI_AUTH_WPA2_PSK .auth_mode = WIFI_AUTH_WPA2_PSK
}); });
records.push_back((ApRecord) { records.push_back((ApRecord) {
.ssid = "No place like 127.0.0.1", .ssid = "No place like 127.0.0.1",
.rssi = -67, .rssi = -67,
.channel = 0,
.auth_mode = WIFI_AUTH_WPA2_PSK .auth_mode = WIFI_AUTH_WPA2_PSK
}); });
records.push_back((ApRecord) { records.push_back((ApRecord) {
.ssid = "Pretty fly for a Wi-Fi", .ssid = "Pretty fly for a Wi-Fi",
.rssi = -70, .rssi = -70,
.channel = 0,
.auth_mode = WIFI_AUTH_WPA2_PSK .auth_mode = WIFI_AUTH_WPA2_PSK
}); });
records.push_back((ApRecord) { records.push_back((ApRecord) {
.ssid = "An AP with a really, really long name", .ssid = "An AP with a really, really long name",
.rssi = -80, .rssi = -80,
.channel = 0,
.auth_mode = WIFI_AUTH_WPA2_PSK .auth_mode = WIFI_AUTH_WPA2_PSK
}); });
records.push_back((ApRecord) { records.push_back((ApRecord) {
.ssid = "Bad Reception", .ssid = "Bad Reception",
.rssi = -90, .rssi = -90,
.channel = 0,
.auth_mode = WIFI_AUTH_OPEN .auth_mode = WIFI_AUTH_OPEN
}); });

View File

@ -103,9 +103,8 @@ bool tt_hal_i2c_lock(i2c_port_t port, TickType_t timeout);
/** /**
* Used to unlock an I2C port. * Used to unlock an I2C port.
* This is useful for creating thread-safe I2C calls while calling ESP-IDF directly of third party I2C APIs. * This is useful for creating thread-safe I2C calls while calling ESP-IDF directly of third party I2C APIs.
* @param[in] port the I2C port to unlock
*/ */
bool tt_hal_i2c_unlock(i2c_port_t port); void tt_hal_i2c_unlock(i2c_port_t port);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -11,43 +9,6 @@ typedef unsigned long TickType;
#define TT_MAX_TICKS ((TickType)(~(TickType)0)) #define TT_MAX_TICKS ((TickType)(~(TickType)0))
/**
* Stall the current task for the specified amount of time.
* @param milliseconds the time in milliseconds to stall.
*/
void tt_kernel_delay_millis(uint32_t milliseconds);
/**
* Stall the current task for the specified amount of time.
* @param milliseconds the time in microsends to stall.
*/
void tt_kernel_delay_micros(uint32_t microSeconds);
/**
* Stall the current task for the specified amount of time.
* @param milliseconds the time in ticks to stall.
*/
void tt_kernel_delay_ticks(TickType ticks);
/** @return the number of ticks since the device was started */
TickType tt_kernel_get_ticks();
/** Convert milliseconds to ticks */
TickType tt_kernel_millis_to_ticks(uint32_t milliSeconds);
/** Stall the current task until the specified timestamp
* @return false if for some reason the delay was broken off
*/
bool tt_kernel_delay_until_tick(TickType tick);
/** @return the tick frequency of the kernel (commonly 1000 Hz when running FreeRTOS) */
uint32_t tt_kernel_get_tick_frequency();
/** @return the number of milliseconds that have passed since the device was started */
uint32_t tt_kernel_get_millis();
unsigned long tt_kernel_get_micros();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -41,9 +41,8 @@ bool tt_lock_acquire(LockHandle handle, TickType timeout);
/** /**
* Attempt to unlock the lock. * Attempt to unlock the lock.
* @param[in] handle the handle that represents the mutex instance * @param[in] handle the handle that represents the mutex instance
* @return true when the lock was unlocked
*/ */
bool tt_lock_release(LockHandle handle); void tt_lock_release(LockHandle handle);
/** Free the memory for this lock /** Free the memory for this lock
* This does not auto-release the lock. * This does not auto-release the lock.

View File

@ -41,23 +41,13 @@ bool tt_message_queue_put(MessageQueueHandle handle, const void* message, TickTy
*/ */
bool tt_message_queue_get(MessageQueueHandle handle, void* message, TickType_t timeout); bool tt_message_queue_get(MessageQueueHandle handle, void* message, TickType_t timeout);
/** @return the total amount of messages that this queue can hold */
uint32_t tt_message_queue_get_capacity(MessageQueueHandle handle);
/** @return the size of a single message in the queue */
uint32_t tt_message_queue_get_message_size(MessageQueueHandle handle);
/** @return the current amount of items in the queue */ /** @return the current amount of items in the queue */
uint32_t tt_message_queue_get_count(MessageQueueHandle handle); uint32_t tt_message_queue_get_count(MessageQueueHandle handle);
/** @return the remaining capacity in the queue */
uint32_t tt_message_queue_get_space(MessageQueueHandle handle);
/** /**
* Remove all items from the queue (if any) * Remove all items from the queue (if any)
* @return true on failure
*/ */
bool tt_message_queue_reset(MessageQueueHandle handle); void tt_message_queue_reset(MessageQueueHandle handle);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -27,7 +27,7 @@ typedef enum {
} ThreadState; } ThreadState;
/** The identifier that represents the thread */ /** The identifier that represents the thread */
typedef TaskHandle_t ThreadId; typedef TaskHandle_t TaskHandle;
/** ThreadCallback Your callback to run in new thread /** ThreadCallback Your callback to run in new thread
* @warning never use osThreadExit in Thread * @warning never use osThreadExit in Thread
@ -132,16 +132,16 @@ void tt_thread_start(ThreadHandle handle);
/** /**
* Wait (block) for the thread to finish. * Wait (block) for the thread to finish.
* @param[in] handle the thread instance handle * @param[in] handle the thread instance handle
* @warning make sure you manually interrupt any logic in your thread (e.g. by an EventFlag or boolean+Mutex) * @warning make sure you manually interrupt any logic in your thread (e.g. by an EventGroup or boolean+Mutex)
*/ */
bool tt_thread_join(ThreadHandle handle, TickType_t timeout); bool tt_thread_join(ThreadHandle handle, TickType_t timeout);
/** /**
* Get thread id * Get thread task handle
* @param[in] handle the thread instance handle * @param[in] handle the thread instance handle
* @return the ThreadId of a thread * @return the task handle of a thread
* */ * */
ThreadId tt_thread_get_id(ThreadHandle handle); TaskHandle tt_thread_get_task_handle(ThreadHandle handle);
/** /**
* Get the return code of a thread * Get the return code of a thread

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "tt_kernel.h"
#include "tt_thread.h" #include "tt_thread.h"
#include <freertos/FreeRTOS.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -15,8 +15,8 @@ typedef void* TimerHandle;
/** The behaviour of the timer */ /** The behaviour of the timer */
typedef enum { typedef enum {
TimerTypeOnce = 0, ///< One-shot timer. TimerTypeOnce = 0, // Timer triggers once after time has passed
TimerTypePeriodic = 1 ///< Repeating timer. TimerTypePeriodic = 1 // Timer triggers repeatedly after time has passed
} TimerType; } TimerType;
typedef void (*TimerCallback)(void* context); typedef void (*TimerCallback)(void* context);
@ -27,7 +27,7 @@ typedef void (*TimerPendingCallback)(void* context, uint32_t arg);
* @param[in] callback the callback to call when the timer expires * @param[in] callback the callback to call when the timer expires
* @param[in] callbackContext the data to pass to the callback * @param[in] callbackContext the data to pass to the callback
*/ */
TimerHandle tt_timer_alloc(TimerType type, TimerCallback callback, void* callbackContext); TimerHandle tt_timer_alloc(TimerType type, TickType ticks, TimerCallback callback, void* callbackContext);
/** Free up the memory of a timer instance */ /** Free up the memory of a timer instance */
void tt_timer_free(TimerHandle handle); void tt_timer_free(TimerHandle handle);
@ -35,18 +35,24 @@ void tt_timer_free(TimerHandle handle);
/** /**
* Start the timer * Start the timer
* @param[in] handle the timer instance handle * @param[in] handle the timer instance handle
* @parma[in] interval the interval of the timer
* @return true when the timer was successfully started * @return true when the timer was successfully started
*/ */
bool tt_timer_start(TimerHandle handle, TickType_t interval); bool tt_timer_start(TimerHandle handle);
/** /**
* Restart an already started timer * Restart an already started timer
* @param[in] handle the timer instance handle * @param[in] handle the timer instance handle
* @parma[in] interval the interval of the timer * @param[in] interval the timer new interval
* @return true when the timer was successfully restarted * @return true when the timer was successfully restarted
*/ */
bool tt_timer_restart(TimerHandle handle, TickType_t interval); bool tt_timer_reset_with_interval(TimerHandle handle, TickType interval);
/**
* Restart an already started timer
* @param[in] handle the timer instance handle
* @return true when the timer was successfully restarted
*/
bool tt_timer_reset(TimerHandle handle);
/** /**
* Stop a started timer * Stop a started timer
@ -63,11 +69,11 @@ bool tt_timer_stop(TimerHandle handle);
bool tt_timer_is_running(TimerHandle handle); bool tt_timer_is_running(TimerHandle handle);
/** /**
* Get the expire time of a timer * Get the expiry time of a timer
* @param[in] handle the timer instance handle * @param[in] handle the timer instance handle
* @return the absolute timestamp at which the timer will expire * @return the absolute timestamp at which the timer will expire
*/ */
uint32_t tt_timer_get_expire_time(TimerHandle handle); uint32_t tt_timer_get_expiry_time(TimerHandle handle);
/** /**
* Set the pending callback for a timer * Set the pending callback for a timer

View File

@ -3,7 +3,7 @@
#include <symbols/freertos.h> #include <symbols/freertos.h>
#include <Tactility/RtosCompat.h> #include <Tactility/freertoscompat/RTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include <freertos/event_groups.h> #include <freertos/event_groups.h>

View File

@ -3,7 +3,7 @@
#include <Tactility/app/AppPaths.h> #include <Tactility/app/AppPaths.h>
#include <Tactility/app/AppContext.h> #include <Tactility/app/AppContext.h>
#include <Tactility/app/ElfApp.h> #include <Tactility/app/ElfApp.h>
#include <Tactility/file/FileLock.h> #include <Tactility/Log.h>
#include <cstring> #include <cstring>

View File

@ -43,8 +43,8 @@ bool tt_hal_i2c_lock(i2c_port_t port, TickType_t timeout) {
return tt::hal::i2c::getLock(port).lock(timeout); return tt::hal::i2c::getLock(port).lock(timeout);
} }
bool tt_hal_i2c_unlock(i2c_port_t port) { void tt_hal_i2c_unlock(i2c_port_t port) {
return tt::hal::i2c::getLock(port).unlock(); tt::hal::i2c::getLock(port).unlock();
} }
} }

View File

@ -12,7 +12,6 @@
#include "tt_hal_i2c.h" #include "tt_hal_i2c.h"
#include "tt_hal_touch.h" #include "tt_hal_touch.h"
#include "tt_hal_uart.h" #include "tt_hal_uart.h"
#include "tt_kernel.h"
#include <tt_lock.h> #include <tt_lock.h>
#include "tt_lvgl.h" #include "tt_lvgl.h"
#include "tt_lvgl_keyboard.h" #include "tt_lvgl_keyboard.h"
@ -261,15 +260,6 @@ const esp_elfsym main_symbols[] {
ESP_ELFSYM_EXPORT(tt_hal_uart_set_baud_rate), ESP_ELFSYM_EXPORT(tt_hal_uart_set_baud_rate),
ESP_ELFSYM_EXPORT(tt_hal_uart_get_baud_rate), ESP_ELFSYM_EXPORT(tt_hal_uart_get_baud_rate),
ESP_ELFSYM_EXPORT(tt_hal_uart_flush_input), ESP_ELFSYM_EXPORT(tt_hal_uart_flush_input),
ESP_ELFSYM_EXPORT(tt_kernel_delay_millis),
ESP_ELFSYM_EXPORT(tt_kernel_delay_micros),
ESP_ELFSYM_EXPORT(tt_kernel_delay_ticks),
ESP_ELFSYM_EXPORT(tt_kernel_get_ticks),
ESP_ELFSYM_EXPORT(tt_kernel_millis_to_ticks),
ESP_ELFSYM_EXPORT(tt_kernel_delay_until_tick),
ESP_ELFSYM_EXPORT(tt_kernel_get_tick_frequency),
ESP_ELFSYM_EXPORT(tt_kernel_get_millis),
ESP_ELFSYM_EXPORT(tt_kernel_get_micros),
ESP_ELFSYM_EXPORT(tt_lvgl_is_started), ESP_ELFSYM_EXPORT(tt_lvgl_is_started),
ESP_ELFSYM_EXPORT(tt_lvgl_lock), ESP_ELFSYM_EXPORT(tt_lvgl_lock),
ESP_ELFSYM_EXPORT(tt_lvgl_unlock), ESP_ELFSYM_EXPORT(tt_lvgl_unlock),
@ -295,8 +285,6 @@ const esp_elfsym main_symbols[] {
ESP_ELFSYM_EXPORT(tt_message_queue_free), ESP_ELFSYM_EXPORT(tt_message_queue_free),
ESP_ELFSYM_EXPORT(tt_message_queue_put), ESP_ELFSYM_EXPORT(tt_message_queue_put),
ESP_ELFSYM_EXPORT(tt_message_queue_get), ESP_ELFSYM_EXPORT(tt_message_queue_get),
ESP_ELFSYM_EXPORT(tt_message_queue_get_capacity),
ESP_ELFSYM_EXPORT(tt_message_queue_get_message_size),
ESP_ELFSYM_EXPORT(tt_message_queue_get_count), ESP_ELFSYM_EXPORT(tt_message_queue_get_count),
ESP_ELFSYM_EXPORT(tt_message_queue_reset), ESP_ELFSYM_EXPORT(tt_message_queue_reset),
ESP_ELFSYM_EXPORT(tt_preferences_alloc), ESP_ELFSYM_EXPORT(tt_preferences_alloc),
@ -324,15 +312,16 @@ const esp_elfsym main_symbols[] {
ESP_ELFSYM_EXPORT(tt_thread_get_state), ESP_ELFSYM_EXPORT(tt_thread_get_state),
ESP_ELFSYM_EXPORT(tt_thread_start), ESP_ELFSYM_EXPORT(tt_thread_start),
ESP_ELFSYM_EXPORT(tt_thread_join), ESP_ELFSYM_EXPORT(tt_thread_join),
ESP_ELFSYM_EXPORT(tt_thread_get_id), ESP_ELFSYM_EXPORT(tt_thread_get_task_handle),
ESP_ELFSYM_EXPORT(tt_thread_get_return_code), ESP_ELFSYM_EXPORT(tt_thread_get_return_code),
ESP_ELFSYM_EXPORT(tt_timer_alloc), ESP_ELFSYM_EXPORT(tt_timer_alloc),
ESP_ELFSYM_EXPORT(tt_timer_free), ESP_ELFSYM_EXPORT(tt_timer_free),
ESP_ELFSYM_EXPORT(tt_timer_start), ESP_ELFSYM_EXPORT(tt_timer_start),
ESP_ELFSYM_EXPORT(tt_timer_restart), ESP_ELFSYM_EXPORT(tt_timer_reset),
ESP_ELFSYM_EXPORT(tt_timer_reset_with_interval),
ESP_ELFSYM_EXPORT(tt_timer_stop), ESP_ELFSYM_EXPORT(tt_timer_stop),
ESP_ELFSYM_EXPORT(tt_timer_is_running), ESP_ELFSYM_EXPORT(tt_timer_is_running),
ESP_ELFSYM_EXPORT(tt_timer_get_expire_time), ESP_ELFSYM_EXPORT(tt_timer_get_expiry_time),
ESP_ELFSYM_EXPORT(tt_timer_set_pending_callback), ESP_ELFSYM_EXPORT(tt_timer_set_pending_callback),
ESP_ELFSYM_EXPORT(tt_timer_set_thread_priority), ESP_ELFSYM_EXPORT(tt_timer_set_thread_priority),
ESP_ELFSYM_EXPORT(tt_timezone_set), ESP_ELFSYM_EXPORT(tt_timezone_set),

View File

@ -1,42 +0,0 @@
#include "tt_kernel.h"
#include <Tactility/kernel/Kernel.h>
extern "C" {
void tt_kernel_delay_millis(uint32_t milliseconds) {
tt::kernel::delayMillis(milliseconds);
}
void tt_kernel_delay_micros(uint32_t microSeconds) {
tt::kernel::delayMicros(microSeconds);
}
void tt_kernel_delay_ticks(TickType ticks) {
tt::kernel::delayTicks((TickType_t)ticks);
}
TickType tt_kernel_get_ticks() {
return tt::kernel::getTicks();
}
TickType tt_kernel_millis_to_ticks(uint32_t milliSeconds) {
return tt::kernel::millisToTicks(milliSeconds);
}
bool tt_kernel_delay_until_tick(TickType tick) {
return tt::kernel::delayUntilTick(tick);
}
uint32_t tt_kernel_get_tick_frequency() {
return tt::kernel::getTickFrequency();
}
uint32_t tt_kernel_get_millis() {
return tt::kernel::getMillis();
}
unsigned long tt_kernel_get_micros() {
return tt::kernel::getMicros();
}
}

View File

@ -32,7 +32,7 @@ bool tt_lock_acquire(LockHandle handle, TickType timeout) {
return HANDLE_AS_LOCK(handle)->lock(timeout); return HANDLE_AS_LOCK(handle)->lock(timeout);
} }
bool tt_lock_release(LockHandle handle) { void tt_lock_release(LockHandle handle) {
return HANDLE_AS_LOCK(handle)->unlock(); return HANDLE_AS_LOCK(handle)->unlock();
} }

View File

@ -21,23 +21,11 @@ bool tt_message_queue_get(MessageQueueHandle handle, void* message, TickType_t t
return HANDLE_TO_MESSAGE_QUEUE(handle)->get(message, timeout); return HANDLE_TO_MESSAGE_QUEUE(handle)->get(message, timeout);
} }
uint32_t tt_message_queue_get_capacity(MessageQueueHandle handle) {
return HANDLE_TO_MESSAGE_QUEUE(handle)->getCapacity();
}
uint32_t tt_message_queue_get_message_size(MessageQueueHandle handle) {
return HANDLE_TO_MESSAGE_QUEUE(handle)->getMessageSize();
}
uint32_t tt_message_queue_get_count(MessageQueueHandle handle) { uint32_t tt_message_queue_get_count(MessageQueueHandle handle) {
return HANDLE_TO_MESSAGE_QUEUE(handle)->getCount(); return HANDLE_TO_MESSAGE_QUEUE(handle)->getCount();
} }
uint32_t tt_message_queue_get_space(MessageQueueHandle handle) { void tt_message_queue_reset(MessageQueueHandle handle) {
return HANDLE_TO_MESSAGE_QUEUE(handle)->getSpace();
}
bool tt_message_queue_reset(MessageQueueHandle handle) {
return HANDLE_TO_MESSAGE_QUEUE(handle)->reset(); return HANDLE_TO_MESSAGE_QUEUE(handle)->reset();
} }

View File

@ -66,8 +66,8 @@ bool tt_thread_join(ThreadHandle handle, TickType_t timeout) {
return HANDLE_AS_THREAD(handle)->join(timeout); return HANDLE_AS_THREAD(handle)->join(timeout);
} }
ThreadId tt_thread_get_id(ThreadHandle handle) { TaskHandle tt_thread_get_task_handle(ThreadHandle handle) {
return HANDLE_AS_THREAD(handle)->getId(); return HANDLE_AS_THREAD(handle)->getTaskHandle();
} }
int32_t tt_thread_get_return_code(ThreadHandle handle) { int32_t tt_thread_get_return_code(ThreadHandle handle) {

View File

@ -9,9 +9,9 @@ struct TimerWrapper {
extern "C" { extern "C" {
TimerHandle tt_timer_alloc(TimerType type, TimerCallback callback, void* callbackContext) { TimerHandle tt_timer_alloc(TimerType type, TickType ticks, TimerCallback callback, void* callbackContext) {
auto wrapper = new TimerWrapper; auto wrapper = new TimerWrapper;
wrapper->timer = std::make_unique<tt::Timer>(static_cast<tt::Timer::Type>(type), [callback, callbackContext](){ callback(callbackContext); }); wrapper->timer = std::make_unique<tt::Timer>(static_cast<tt::Timer::Type>(type), ticks, [callback, callbackContext](){ callback(callbackContext); });
return wrapper; return wrapper;
} }
@ -21,12 +21,16 @@ void tt_timer_free(TimerHandle handle) {
delete wrapper; delete wrapper;
} }
bool tt_timer_start(TimerHandle handle, TickType_t intervalTicks) { bool tt_timer_start(TimerHandle handle) {
return HANDLE_TO_WRAPPER(handle)->timer->start(intervalTicks); return HANDLE_TO_WRAPPER(handle)->timer->start();
} }
bool tt_timer_restart(TimerHandle handle, TickType_t intervalTicks) { bool tt_timer_reset(TimerHandle handle) {
return HANDLE_TO_WRAPPER(handle)->timer->restart(intervalTicks); return HANDLE_TO_WRAPPER(handle)->timer->reset();
}
bool tt_timer_reset_with_interval(TimerHandle handle, TickType interval) {
return HANDLE_TO_WRAPPER(handle)->timer->reset(interval);
} }
bool tt_timer_stop(TimerHandle handle) { bool tt_timer_stop(TimerHandle handle) {
@ -37,8 +41,8 @@ bool tt_timer_is_running(TimerHandle handle) {
return HANDLE_TO_WRAPPER(handle)->timer->isRunning(); return HANDLE_TO_WRAPPER(handle)->timer->isRunning();
} }
uint32_t tt_timer_get_expire_time(TimerHandle handle) { uint32_t tt_timer_get_expiry_time(TimerHandle handle) {
return HANDLE_TO_WRAPPER(handle)->timer->getExpireTime(); return HANDLE_TO_WRAPPER(handle)->timer->getExpiryTime();
} }
bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeoutTicks) { bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeoutTicks) {
@ -46,12 +50,12 @@ bool tt_timer_set_pending_callback(TimerHandle handle, TimerPendingCallback call
callback, callback,
callbackContext, callbackContext,
callbackArg, callbackArg,
(TickType_t)timeoutTicks timeoutTicks
); );
} }
void tt_timer_set_thread_priority(TimerHandle handle, ThreadPriority priority) { void tt_timer_set_thread_priority(TimerHandle handle, ThreadPriority priority) {
HANDLE_TO_WRAPPER(handle)->timer->setThreadPriority(static_cast<tt::Thread::Priority>(priority)); HANDLE_TO_WRAPPER(handle)->timer->setCallbackPriority(static_cast<tt::Thread::Priority>(priority));
} }
} }

View File

@ -9,7 +9,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Include/" INCLUDE_DIRS "Include/"
REQUIRES mbedtls nvs_flash esp_rom esp_timer REQUIRES TactilityFreeRtos mbedtls nvs_flash esp_rom
) )
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@ -32,7 +32,7 @@ else()
add_definitions(-D_Nonnull=) add_definitions(-D_Nonnull=)
target_link_libraries(TactilityCore target_link_libraries(TactilityCore
PUBLIC TactilityFreeRtos
PUBLIC mbedtls PUBLIC mbedtls
PUBLIC freertos_kernel
) )
endif() endif()

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "RtosCompat.h" #include <Tactility/freertoscompat/RTOS.h>
namespace tt { namespace tt {

View File

@ -1,56 +0,0 @@
/**
* @file Dispatcher.h
*
* Dispatcher is a thread-safe code execution queue.
*/
#pragma once
#include "MessageQueue.h"
#include "Mutex.h"
#include "EventFlag.h"
#include <memory>
#include <queue>
namespace tt {
/**
* A thread-safe way to defer code execution.
* Generally, one task would dispatch the execution,
* while the other thread consumes and executes the work.
*/
class Dispatcher final {
public:
typedef std::function<void()> Function;
private:
Mutex mutex;
std::queue<Function> queue = {};
EventFlag eventFlag;
public:
explicit Dispatcher() = default;
~Dispatcher();
/**
* Queue a function to be consumed elsewhere.
* @param[in] function the function to execute elsewhere
* @param[in] timeout lock acquisition timeout
* @return true if dispatching was successful (timeout not reached)
*/
bool dispatch(Function function, TickType_t timeout = portMAX_DELAY);
/**
* Consume 1 or more dispatched function (if any) until the queue is empty.
* @warning The timeout is only the wait time before consuming the message! It is not a limit to the total execution time when calling this method.
* @param[in] timeout the ticks to wait for a message
* @return the amount of messages that were consumed
*/
uint32_t consume(TickType_t timeout = portMAX_DELAY);
};
} // namespace

View File

@ -1,36 +0,0 @@
#pragma once
#include "Dispatcher.h"
namespace tt {
/** Starts a Thread to process dispatched messages */
class DispatcherThread final {
Dispatcher dispatcher;
std::unique_ptr<Thread> thread;
bool interruptThread = true;
int32_t threadMain();
public:
explicit DispatcherThread(const std::string& threadName, size_t threadStackSize = 4096);
~DispatcherThread();
/**
* Dispatch a message.
*/
bool dispatch(Dispatcher::Function function, TickType_t timeout = portMAX_DELAY);
/** Start the thread (blocking). */
void start();
/** Stop the thread (blocking). */
void stop();
/** @return true of the thread is started */
bool isStarted() const { return thread != nullptr && !interruptThread; }
};
}

View File

@ -1,60 +0,0 @@
#pragma once
#include "RtosCompatEventGroups.h"
#include <memory>
namespace tt {
/**
* Wrapper for FreeRTOS xEventGroup.
*/
class EventFlag final {
struct EventGroupHandleDeleter {
void operator()(EventGroupHandle_t handleToDelete) {
vEventGroupDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<EventGroupHandle_t>, EventGroupHandleDeleter> handle;
public:
EventFlag();
~EventFlag();
enum Flag {
WaitAny = 0x00000000U, ///< Wait for any flag (default).
WaitAll = 0x00000001U, ///< Wait for all flags.
NoClear = 0x00000002U, ///< Do not clear flags which have been specified to wait for.
Error = 0x80000000U, ///< Error indicator.
ErrorUnknown = 0xFFFFFFFFU, ///< TtStatusError (-1).
ErrorTimeout = 0xFFFFFFFEU, ///< TtStatusErrorTimeout (-2).
ErrorResource = 0xFFFFFFFDU, ///< TtStatusErrorResource (-3).
ErrorParameter = 0xFFFFFFFCU, ///< TtStatusErrorParameter (-4).
ErrorISR = 0xFFFFFFFAU, ///< TtStatusErrorISR (-6).
};
/** Set the bitmask for 1 or more flags that we might be waiting for */
uint32_t set(uint32_t flags) const;
/** Clear the specified flags */
uint32_t clear(uint32_t flags) const;
/** Get the currently set flags */
uint32_t get() const;
/** Await for flags to be set
* @param[in] flags the bitmask of the flags that we want to wait for
* @param[in] options the trigger behaviour: WaitAny, WaitAll, NoClear (NoClear can be combined with either WaitAny or WaitAll)
* @param[in] timeoutTicks the maximum amount of ticks to wait
*/
uint32_t wait(
uint32_t flags,
uint32_t options = WaitAny,
uint32_t timeoutTicks = (uint32_t)portMAX_DELAY
) const;
};
} // namespace

View File

@ -1,86 +0,0 @@
/**
* @file MessageQueue.h
*
* MessageQueue is a wrapper for FreeRTOS xQueue functionality.
* There is no additional thread-safety on top of the xQueue functionality,
* so make sure you create a lock if needed.
*/
#pragma once
#include <memory>
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#else
#include "FreeRTOS.h"
#include "queue.h"
#endif
namespace tt {
/**
* Message Queue implementation.
* Calls can be done from ISR/IRQ mode unless otherwise specified.
*/
class MessageQueue {
struct QueueHandleDeleter {
void operator()(QueueHandle_t handleToDelete) {
vQueueDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, QueueHandleDeleter> handle;
public:
/** Allocate message queue
* @param[in] capacity Maximum messages in queue
* @param[in] messageSize The size in bytes of a single message
*/
MessageQueue(uint32_t capacity, uint32_t messageSize);
~MessageQueue();
/** Post a message to the queue.
* The message is queued by copy, not by reference.
* @param[in] message A pointer to a message. The message will be copied into a buffer.
* @param[in] timeout
* @return success result
*/
bool put(const void* message, TickType_t timeout);
/** Get message from queue
* @param[out] message A pointer to an already allocated message object
* @param[in] timeout
* @return success result
*/
bool get(void* message, TickType_t timeout);
/**
* @return The maximum amount of messages that can be in the queue at any given time.
*/
uint32_t getCapacity() const;
/**
* @return The size of a single message in bytes
*/
uint32_t getMessageSize() const;
/**
* @return How many messages are currently in the queue.
*/
uint32_t getCount() const;
/**
* @return How many messages can be added to the queue before the put() method starts blocking.
*/
uint32_t getSpace() const;
/** Reset queue (cannot be called in ISR/IRQ mode)
* @return success result
*/
bool reset();
};
} // namespace

View File

@ -1,13 +0,0 @@
#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

@ -1,14 +0,0 @@
#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

@ -1,14 +0,0 @@
#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

@ -1,14 +0,0 @@
#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

@ -1,13 +0,0 @@
#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,69 +0,0 @@
#pragma once
#include "Lock.h"
#include "kernel/Kernel.h"
#include <cassert>
#include <memory>
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#else
#include "FreeRTOS.h"
#include "semphr.h"
#endif
namespace tt {
/**
* Wrapper for xSemaphoreCreateBinary (max count == 1) and xSemaphoreCreateCounting (max count > 1)
* Can be used from IRQ/ISR mode, but cannot be created/destroyed from such a context.
*/
class Semaphore final : public Lock {
struct SemaphoreHandleDeleter {
void operator()(QueueHandle_t handleToDelete) {
assert(!kernel::isIsr());
vSemaphoreDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<QueueHandle_t>, SemaphoreHandleDeleter> handle;
public:
using Lock::lock;
/**
* Cannot be called from IRQ/ISR mode.
* @param[in] maxAvailable The maximum count
* @param[in] initialAvailable The initial count
*/
Semaphore(uint32_t maxAvailable, uint32_t initialAvailable);
/**
* Cannot be called from IRQ/ISR mode.
* @param[in] maxAvailable The maximum count
*/
explicit Semaphore(uint32_t maxAvailable) : Semaphore(maxAvailable, maxAvailable) {};
/** Cannot be called from IRQ/ISR mode. */
~Semaphore() override;
Semaphore(Semaphore& other) : handle(std::move(other.handle)) {}
/** Acquire semaphore */
bool acquire(TickType_t timeout) const;
/** Release semaphore */
bool release() const;
bool lock(TickType_t timeout) const override { return acquire(timeout); }
bool unlock() const override { return release(); }
/** @return return the amount of times this semaphore can be acquired/locked */
uint32_t getAvailable() const;
};
} // namespace

View File

@ -1,152 +0,0 @@
#pragma once
#include <memory>
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include "freertos/stream_buffer.h"
#else
#include "FreeRTOS.h"
#include "stream_buffer.h"
#endif
namespace tt {
/**
* Stream buffers are used to send a continuous stream of data from one task or
* interrupt to another. Their implementation is light weight, making them
* particularly suited for interrupt to task and core to core communication
* scenarios.
*
* **NOTE**: Stream buffer implementation assumes there is only one task or
* interrupt that will write to the buffer (the writer), and only one task or
* interrupt that will read from the buffer (the reader).
*/
class StreamBuffer final {
struct StreamBufferHandleDeleter {
void operator()(StreamBufferHandle_t handleToDelete) {
vStreamBufferDelete(handleToDelete);
}
};
std::unique_ptr<std::remove_pointer_t<StreamBufferHandle_t>, StreamBufferHandleDeleter> handle;
public:
/**
* Stream buffer implementation assumes there is only one task or
* interrupt that will write to the buffer (the writer), and only one task or
* interrupt that will read from the buffer (the reader).
*
* @param[in] size The total number of bytes the stream buffer will be able to hold at any one time.
* @param[in] triggerLevel The number of bytes that must be in the stream buffer
* before a task that is blocked on the stream buffer to wait for data is moved out of the blocked state.
* @return The stream buffer instance.
*/
StreamBuffer(size_t size, size_t triggerLevel);
~StreamBuffer() = default;
/**
* @brief Set trigger level for stream buffer.
* A stream buffer's trigger level is the number of bytes that must be in the
* stream buffer before a task that is blocked on the stream buffer to
* wait for data is moved out of the blocked state.
*
* @param[in] triggerLevel The new trigger level for the stream buffer.
* @return true if trigger level can be be updated (new trigger level was less than or equal to the stream buffer's length).
* @return false if trigger level can't be be updated (new trigger level was greater than the stream buffer's length).
*/
bool setTriggerLevel(size_t triggerLevel) const;
/**
* @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer.
* Wakes up task waiting for data to become available if called from ISR.
*
* @param[in] data A pointer to the data that is to be copied into the stream buffer.
* @param[in] length The maximum number of bytes to copy from data into the stream buffer.
* @param[in] timeout The maximum amount of time the task should remain in the
* Blocked state to wait for space to become available if the stream buffer is full.
* Will return immediately if timeout is zero.
* Setting timeout to portMAX_DELAY will cause the task to wait indefinitely.
* Ignored if called from ISR.
* @return The number of bytes actually written to the stream buffer.
*/
size_t send(
const void* data,
size_t length,
uint32_t timeout
) const;
/**
* @brief Receives bytes from a stream buffer.
* Wakes up task waiting for space to become available if called from ISR.
*
* @param[in] data A pointer to the buffer into which the received bytes will be
* copied.
* @param[in] length The length of the buffer pointed to by the data parameter.
* @param[in] timeout The maximum amount of time the task should remain in the
* Blocked state to wait for data to become available if the stream buffer is empty.
* Will return immediately if timeout is zero.
* Setting timeout to portMAX_DELAY will cause the task to wait indefinitely.
* Ignored if called from ISR.
* @return The number of bytes read from the stream buffer, if any.
*/
size_t receive(
void* data,
size_t length,
uint32_t timeout
) const;
/**
* @brief Queries a stream buffer to see how much data it contains, which is equal to
* the number of bytes that can be read from the stream buffer before the stream
* buffer would be empty.
*
* @return The number of bytes that can be read from the stream buffer before
* the stream buffer would be empty.
*/
size_t getAvailableReadBytes() const;
/**
* @brief Queries a stream buffer to see how much free space it contains, which is
* equal to the amount of data that can be sent to the stream buffer before it
* is full.
*
* @return The number of bytes that can be written to the stream buffer before
* the stream buffer would be full.
*/
size_t getAvailableWriteBytes() const;
/**
* @brief Queries a stream buffer to see if it is full.
*
* @return true if the stream buffer is full.
* @return false if the stream buffer is not full.
*/
bool isFull() const;
/**
* @brief Queries a stream buffer to see if it is empty.
*
* @param stream_buffer The stream buffer instance.
* @return true if the stream buffer is empty.
* @return false if the stream buffer is not empty.
*/
bool isEmpty() const;
/**
* @brief Resets a stream buffer to its initial, empty, state. Any data that was
* in the stream buffer is discarded. A stream buffer can only be reset if there
* are no tasks blocked waiting to either send to or receive from the stream buffer.
*
* @return TtStatusOk if the stream buffer is reset.
* @return TtStatusError if there was a task blocked waiting to send to or read
* from the stream buffer then the stream buffer is not reset.
*/
bool reset() const;
};
} // namespace

View File

@ -2,11 +2,9 @@
#include <cstdio> #include <cstdio>
#include "Tactility/Thread.h"
#include "Check.h" #include "Check.h"
#include "CoreDefines.h" #include "CoreDefines.h"
#include "EventFlag.h"
#include "kernel/Kernel.h"
#include "kernel/critical/Critical.h"
#include "Log.h" #include "Log.h"
#include "Mutex.h"
#include "Thread.h" #include <Tactility/kernel/Kernel.h>

View File

@ -1,3 +0,0 @@
#pragma once
#define TT_CONFIG_THREAD_MAX_PRIORITIES 10

View File

@ -1,195 +0,0 @@
#pragma once
#include "RtosCompatTask.h"
#include <functional>
#include <memory>
#include <string>
namespace tt {
typedef TaskHandle_t ThreadId;
class Thread final {
public:
enum class State{
Stopped,
Starting,
Running,
};
/** ThreadPriority */
enum class Priority : UBaseType_t {
None = 0U, /**< Uninitialized, choose system default */
Idle = 1U,
Lower = 2U,
Low = 3U,
Normal = 4U,
High = 5U,
Higher = 6U,
Critical = 7U
};
/** ThreadCallback Your callback to run in new thread
* @warning never use osThreadExit in Thread
*/
typedef int32_t (*Callback)(void* context);
typedef std::function<int32_t()> MainFunction;
/** Write to stdout callback
* @param[in] data pointer to data
* @param[in] size data size @warning your handler must consume everything
*/
typedef void (*StdoutWriteCallback)(const char* data, size_t size);
/** Thread state change callback called upon thread state change
* @param[in] state new thread state
* @param[in] context callback context
*/
typedef void (*StateCallback)(State state, void* context);
private:
static void mainBody(void* context);
TaskHandle_t taskHandle = nullptr;
State state = State::Stopped;
MainFunction mainFunction;
int32_t callbackResult = 0;
StateCallback stateCallback = nullptr;
void* stateCallbackContext = nullptr;
std::string name = {};
Priority priority = Priority::Normal;
configSTACK_DEPTH_TYPE stackSize = 0;
portBASE_TYPE affinity = -1;
void setState(Thread::State state);
public:
Thread() = default;
/** Allocate Thread, shortcut version
* @param[in] name the name of the thread
* @param[in] stackSize in bytes
* @param[in] function
* @param[in] affinity Which CPU core to pin this task to, -1 means unpinned (only works on ESP32)
*/
Thread(
std::string name,
configSTACK_DEPTH_TYPE stackSize,
MainFunction function,
portBASE_TYPE affinity = -1
);
~Thread();
/** Set Thread name
* @param[in] name string
*/
void setName(std::string name);
/** Set Thread stack size
* @param[in] stackSize stack size in bytes
*/
void setStackSize(size_t stackSize);
/** Set CPU core pinning for this thread.
* @param[in] affinity -1 means not pinned, otherwise it's the core id (e.g. 0 or 1 on ESP32)
*/
void setAffinity(portBASE_TYPE affinity);
/** Set Thread callback
* @param[in] callback ThreadCallback, called upon thread run
* @param[in] callbackContext what to pass to the callback
*/
[[deprecated("use setMainFunction()")]]
void setCallback(Callback callback, _Nullable void* callbackContext = nullptr);
/** Set Thread callback
* @param[in] function called upon thread run
*/
void setMainFunction(MainFunction function);
/** Set Thread priority
* @param[in] priority ThreadPriority value
*/
void setPriority(Priority priority);
/** Set Thread state change callback
* @param[in] callback state change callback
* @param[in] callbackContext pointer to context
*/
void setStateCallback(StateCallback callback, _Nullable void* callbackContext = nullptr);
/** Get Thread state
* @return thread state from ThreadState
*/
State getState() const;
/** Start Thread */
void start();
/** Join Thread
* @warning make sure you manually interrupt any logic in your thread (e.g. by an EventFlag or boolean+Mutex)
* @param[in] timeout the maximum amount of time to wait
* @param[in] pollInterval the amount of ticks to wait before we check again if the thread is finished
* @return success result
*/
bool join(TickType_t timeout = portMAX_DELAY, TickType_t pollInterval = 10);
/** Get FreeRTOS ThreadId for Thread instance
* @return ThreadId or nullptr
*/
ThreadId getId() const;
/**
* @warning crashes when state is not "stopped"
* @return thread return code
*/
int32_t getReturnCode() const;
/** Suspend thread
* @param[in] threadId thread id
*/
static void suspend(ThreadId threadId);
/** Resume thread
* @param[in] threadId thread id
*/
static void resume(ThreadId threadId);
/** Get thread suspended state
* @param[in] threadId thread id
* @return true if thread is suspended
*/
static bool isSuspended(ThreadId threadId);
/**
* @brief Get thread stack watermark
* @param[in] threadId
* @return uint32_t
*/
static uint32_t getStackSpace(ThreadId threadId);
/** @return pointer to Thread instance or nullptr if this thread doesn't belong to Tactility */
static Thread* getCurrent();
static uint32_t setFlags(ThreadId threadId, uint32_t flags);
static uint32_t clearFlags(uint32_t flags);
static uint32_t getFlags();
static uint32_t awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout);
};
constexpr auto THREAD_PRIORITY_SERVICE = Thread::Priority::High;
constexpr auto THREAD_PRIORITY_RENDER = Thread::Priority::Higher;
constexpr auto THREAD_PRIORITY_ISR = Thread::Priority::Critical;
} // namespace

View File

@ -1,98 +0,0 @@
#pragma once
#include "RtosCompatTimers.h"
#include "Thread.h"
#include <memory>
#include <functional>
namespace tt {
class Timer {
public:
typedef std::function<void()> Callback;
typedef void (*PendingCallback)(void* context, uint32_t arg);
private:
struct TimerHandleDeleter {
void operator()(TimerHandle_t handleToDelete) const {
xTimerDelete(handleToDelete, portMAX_DELAY);
}
};
Callback callback;
std::unique_ptr<std::remove_pointer_t<TimerHandle_t>, TimerHandleDeleter> handle;
static void onCallback(TimerHandle_t hTimer);
public:
enum class Type {
Once = 0, ///< One-shot timer.
Periodic = 1 ///< Repeating timer.
};
enum class Priority{
Normal, /**< Lower then other threads */
Elevated, /**< Same as other threads */
};
/**
* @param[in] type The timer type
* @param[in] callback The callback function
*/
Timer(Type type, Callback callback);
~Timer();
/** Start timer
* @warning This is asynchronous call, real operation will happen as soon as timer service process this request.
* @param[in] interval The interval in ticks
* @return success result
*/
bool start(TickType_t interval);
/** Restart timer with previous timeout value
* @warning This is asynchronous call, real operation will happen as soon as timer service process this request.
* @param[in] interval The interval in ticks
* @return success result
*/
bool restart(TickType_t interval);
/** Stop timer
* @warning This is asynchronous call, real operation will happen as soon as timer service process this request.
* @return success result
*/
bool stop();
/** Is timer running
* @warning This cal may and will return obsolete timer state if timer commands are still in the queue. Please read FreeRTOS timer documentation first.
* @return true when running
*/
bool isRunning();
/** Get timer expire time
* @return expire tick
*/
TickType_t getExpireTime();
/**
* Calls xTimerPendFunctionCall internally.
* @param[in] callback the function to call
* @param[in] callbackContext the first function argument
* @param[in] callbackArg the second function argument
* @param[in] timeout the function timeout (must set to 0 in ISR mode)
* @return true on success
*/
bool setPendingCallback(PendingCallback callback, void* callbackContext, uint32_t callbackArg, TickType_t timeout);
/** Set Timer thread priority
* @param[in] priority The priority
*/
void setThreadPriority(Thread::Priority priority);
};
} // namespace

View File

@ -4,7 +4,8 @@
*/ */
#pragma once #pragma once
#include "Tactility/TactilityCore.h" #include <Tactility/TactilityCore.h>
#include <Tactility/Lock.h>
#include <cstdio> #include <cstdio>
#include <dirent.h> #include <dirent.h>

View File

@ -1,117 +0,0 @@
#pragma once
#ifdef ESP_PLATFORM
#include "freertos/FreeRTOS.h"
#include <esp_timer.h>
#else
#include "FreeRTOS.h"
#include <sys/time.h>
#endif
namespace tt::kernel {
/** Recognized platform types */
typedef enum {
PlatformEsp,
PlatformSimulator
} Platform;
/** Return true when called from an Interrupt Service Routine (~IRQ mode) */
#ifdef ESP_PLATFORM
constexpr bool isIsr() { return (xPortInIsrContext() == pdTRUE); }
#else
constexpr bool isIsr() { return false; }
#endif
/** Check if kernel is running
* @return true if the FreeRTOS kernel is running, false otherwise
*/
bool isRunning();
/** Lock kernel, pause process scheduling
* @warning don't call from ISR context
* @return true on success
*/
bool lock();
/** Unlock kernel, resume process scheduling
* @warning don't call from ISR context
* @return true on success
*/
bool unlock();
/** Restore kernel lock state
* @warning don't call from ISR context
* @param[in] lock The lock state
* @return true on success
*/
bool restoreLock(bool lock);
/** Get kernel systick frequency
* @return systick counts per second
*/
uint32_t getTickFrequency();
TickType_t getTicks();
constexpr size_t getMillis() { return getTicks() / portTICK_PERIOD_MS; }
constexpr long int getMicros() {
#ifdef ESP_PLATFORM
return static_cast<unsigned long>(esp_timer_get_time());
#else
timeval tv;
gettimeofday(&tv, nullptr);
return 1000000 * tv.tv_sec + tv.tv_usec;
#endif
}
/** Delay execution
* @warning don't call from ISR context
* Also keep in mind delay is aliased to scheduler timer intervals.
* @param[in] ticks The ticks count to pause
*/
void delayTicks(TickType_t ticks);
/** Delay until tick
* @warning don't call from ISR context
* @param[in] ticks The tick until which kerel should delay task execution
* @return true on success
*/
bool delayUntilTick(TickType_t tick);
constexpr TickType_t secondsToTicks(uint32_t seconds) {
return static_cast<TickType_t>(seconds) * 1000U / portTICK_PERIOD_MS;
}
constexpr TickType_t minutesToTicks(uint32_t minutes) {
return secondsToTicks(minutes * 60U);
}
/** Convert milliseconds to ticks
*
* @param[in] milliSeconds time in milliseconds
* @return time in ticks
*/
TickType_t millisToTicks(uint32_t milliSeconds);
/** Delay in milliseconds
* This method uses kernel ticks on the inside, which causes delay to be aliased to scheduler timer intervals.
* Real wait time will be between X+ milliseconds.
* Special value: 0, will cause task yield.
* Also if used when kernel is not running will fall back to delayMicros()
* @warning don't call from ISR context
* @param[in] milliSeconds milliseconds to wait
*/
void delayMillis(uint32_t milliSeconds);
/** Delay in microseconds
* Implemented using Cortex DWT counter. Blocking and non aliased.
* @param[in] microSeconds microseconds to wait
*/
void delayMicros(uint32_t microSeconds);
/** @return the platform that Tactility currently is running on. */
Platform getPlatform();
} // namespace

View File

@ -0,0 +1,14 @@
#pragma once
namespace tt::kernel {
/** Recognized platform types */
typedef enum {
PlatformEsp,
PlatformSimulator
} Platform;
/** @return the platform that Tactility currently is running on. */
Platform getPlatform();
} // namespace

View File

@ -1,9 +1,9 @@
#include "Tactility/Check.h" #include <Tactility/Check.h>
#include "Tactility/Log.h" #include <Tactility/Log.h>
#include "Tactility/RtosCompatTask.h" #include <Tactility/freertoscompat/Task.h>
#define TAG "kernel" constexpr auto TAG = "kernel";
static void logMemoryInfo() { static void logMemoryInfo() {
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM

View File

@ -1,70 +0,0 @@
#include "Tactility/Dispatcher.h"
#include "Tactility/Check.h"
#include "Tactility/kernel/Kernel.h"
namespace tt {
#define TAG "dispatcher"
#define BACKPRESSURE_WARNING_COUNT ((EventBits_t)100)
#define WAIT_FLAG ((EventBits_t)1U)
Dispatcher::~Dispatcher() {
// Wait for Mutex usage
mutex.lock();
mutex.unlock();
}
bool Dispatcher::dispatch(Function function, TickType_t timeout) {
// Mutate
if (mutex.lock(timeout)) {
queue.push(std::move(function));
if (queue.size() == BACKPRESSURE_WARNING_COUNT) {
TT_LOG_W(TAG, "Backpressure: You're not consuming fast enough (100 queued)");
}
tt_check(mutex.unlock());
// Signal
eventFlag.set(WAIT_FLAG);
return true;
} else {
TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED);
return false;
}
}
uint32_t Dispatcher::consume(TickType_t timeout) {
// Wait for signal
uint32_t result = eventFlag.wait(WAIT_FLAG, EventFlag::WaitAny, timeout);
if (result & EventFlag::Error) {
return 0;
}
eventFlag.clear(WAIT_FLAG);
// Mutate
bool processing = true;
uint32_t consumed = 0;
do {
if (mutex.lock(10)) {
if (!queue.empty()) {
auto function = queue.front();
queue.pop();
consumed++;
processing = !queue.empty();
// Don't keep lock as callback might be slow
mutex.unlock();
function();
} else {
processing = false;
mutex.unlock();
}
} else {
TT_LOG_W(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED);
}
} while (processing);
return consumed;
}
} // namespace

View File

@ -1,47 +0,0 @@
#include "Tactility/DispatcherThread.h"
namespace tt {
DispatcherThread::DispatcherThread(const std::string& threadName, size_t threadStackSize) {
thread = std::make_unique<Thread>(
threadName,
threadStackSize,
[this] {
return threadMain();
}
);
}
DispatcherThread::~DispatcherThread() {
if (thread->getState() != Thread::State::Stopped) {
stop();
}
}
int32_t DispatcherThread::threadMain() {
do {
/**
* If this value is too high (e.g. 1 second) then the dispatcher destroys too slowly when the simulator exits.
* This causes the problems with other services doing an update (e.g. Statusbar) and calling into destroyed mutex in the global scope.
*/
dispatcher.consume(100 / portTICK_PERIOD_MS);
} while (!interruptThread);
return 0;
}
bool DispatcherThread::dispatch(Dispatcher::Function function, TickType_t timeout) {
return dispatcher.dispatch(function, timeout);
}
void DispatcherThread::start() {
interruptThread = false;
thread->start();
}
void DispatcherThread::stop() {
interruptThread = true;
thread->join();
}
}

View File

@ -1,135 +0,0 @@
#include "Tactility/EventFlag.h"
#include "Tactility/Check.h"
#include "Tactility/kernel/Kernel.h"
#define TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U
#define TT_EVENT_FLAG_INVALID_BITS (~((1UL << TT_EVENT_FLAG_MAX_BITS_EVENT_GROUPS) - 1U))
namespace tt {
EventFlag::EventFlag() :
handle(xEventGroupCreate())
{
assert(!kernel::isIsr());
tt_check(handle);
}
EventFlag::~EventFlag() {
assert(!kernel::isIsr());
}
uint32_t EventFlag::set(uint32_t flags) const {
assert(handle);
assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
uint32_t rflags;
BaseType_t yield;
if (kernel::isIsr()) {
yield = pdFALSE;
if (xEventGroupSetBitsFromISR(handle.get(), (EventBits_t)flags, &yield) == pdFAIL) {
rflags = (uint32_t)ErrorResource;
} else {
rflags = flags;
portYIELD_FROM_ISR(yield);
}
} else {
rflags = xEventGroupSetBits(handle.get(), (EventBits_t)flags);
}
/* Return event flags after setting */
return rflags;
}
uint32_t EventFlag::clear(uint32_t flags) const {
assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
uint32_t rflags;
if (kernel::isIsr()) {
rflags = xEventGroupGetBitsFromISR(handle.get());
if (xEventGroupClearBitsFromISR(handle.get(), (EventBits_t)flags) == pdFAIL) {
rflags = (uint32_t)ErrorResource;
} else {
/* xEventGroupClearBitsFromISR only registers clear operation in the timer command queue. */
/* Yield is required here otherwise clear operation might not execute in the right order. */
/* See https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/93 for more info. */
portYIELD_FROM_ISR(pdTRUE);
}
} else {
rflags = xEventGroupClearBits(handle.get(), (EventBits_t)flags);
}
/* Return event flags before clearing */
return rflags;
}
uint32_t EventFlag::get() const {
uint32_t rflags;
if (kernel::isIsr()) {
rflags = xEventGroupGetBitsFromISR(handle.get());
} else {
rflags = xEventGroupGetBits(handle.get());
}
/* Return current event flags */
return (rflags);
}
uint32_t EventFlag::wait(
uint32_t flags,
uint32_t options,
uint32_t timeoutTicksw
) const {
assert(!kernel::isIsr());
assert((flags & TT_EVENT_FLAG_INVALID_BITS) == 0U);
BaseType_t wait_all;
BaseType_t exit_clear;
uint32_t rflags;
if (options & WaitAll) {
wait_all = pdTRUE;
} else {
wait_all = pdFALSE;
}
if (options & NoClear) {
exit_clear = pdFALSE;
} else {
exit_clear = pdTRUE;
}
rflags = xEventGroupWaitBits(
handle.get(),
(EventBits_t)flags,
exit_clear,
wait_all,
(TickType_t)timeoutTicksw
);
if (options & WaitAll) {
if ((flags & rflags) != flags) {
if (timeoutTicksw > 0U) {
rflags = (uint32_t)ErrorTimeout;
} else {
rflags = (uint32_t)ErrorResource;
}
}
} else {
if ((flags & rflags) == 0U) {
if (timeoutTicksw > 0U) {
rflags = (uint32_t)ErrorTimeout;
} else {
rflags = (uint32_t)ErrorResource;
}
}
}
return rflags;
}
} // namespace

View File

@ -1,9 +0,0 @@
#include "Tactility/Lock.h"
namespace tt {
ScopedLock Lock::asScopedLock() const {
return ScopedLock(*this);
}
}

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