diff --git a/Boards/LilygoTdeck/CMakeLists.txt b/Boards/LilygoTdeck/CMakeLists.txt index 3c380b69..6b073058 100644 --- a/Boards/LilygoTdeck/CMakeLists.txt +++ b/Boards/LilygoTdeck/CMakeLists.txt @@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*) idf_component_register( SRCS ${SOURCE_FILES} INCLUDE_DIRS "Source" - REQUIRES Tactility EspLcdCompat ST7789 GT911 PwmBacklight driver esp_adc + REQUIRES Tactility EspLcdCompat ST7789 GT911 PwmBacklight EstimatedPower driver ) diff --git a/Boards/LilygoTdeck/Source/Init.cpp b/Boards/LilygoTdeck/Source/Init.cpp index c8c4b6df..57449664 100644 --- a/Boards/LilygoTdeck/Source/Init.cpp +++ b/Boards/LilygoTdeck/Source/Init.cpp @@ -30,7 +30,7 @@ static bool powerOn() { return true; } -bool tdeckInit() { +bool initBoot() { ESP_LOGI(TAG, LOG_MESSAGE_POWER_ON_START); if (!powerOn()) { TT_LOG_E(TAG, LOG_MESSAGE_POWER_ON_FAILED); diff --git a/Boards/LilygoTdeck/Source/LilygoTdeck.cpp b/Boards/LilygoTdeck/Source/LilygoTdeck.cpp index d88e4054..f5de109f 100644 --- a/Boards/LilygoTdeck/Source/LilygoTdeck.cpp +++ b/Boards/LilygoTdeck/Source/LilygoTdeck.cpp @@ -1,24 +1,29 @@ -#include "Tactility/lvgl/LvglSync.h" -#include "hal/TdeckDisplay.h" -#include "hal/TdeckDisplayConstants.h" -#include "hal/TdeckKeyboard.h" -#include "hal/TdeckPower.h" -#include "hal/TdeckSdCard.h" - #include +#include -#define TDECK_SPI_TRANSFER_SIZE_LIMIT (TDECK_LCD_HORIZONTAL_RESOLUTION * TDECK_LCD_SPI_TRANSFER_HEIGHT * (LV_COLOR_DEPTH / 8)) +#include "devices/Display.h" +#include "devices/Power.h" +#include "devices/Sdcard.h" +#include "devices/TdeckKeyboard.h" -bool tdeckInit(); +#define TDECK_SPI_TRANSFER_SIZE_LIMIT (320 * 240 * (LV_COLOR_DEPTH / 8)) + +bool initBoot(); using namespace tt::hal; +static std::vector> createDevices() { + return { + createPower(), + createDisplay(), + std::make_shared(), + createSdCard() + }; +} + extern const Configuration lilygo_tdeck = { - .initBoot = tdeckInit, - .createDisplay = createDisplay, - .createKeyboard = createKeyboard, - .sdcard = createTdeckSdCard(), - .power = tdeck_get_power, + .initBoot = initBoot, + .createDevices = createDevices, .i2c = { i2c::Configuration { .name = "Internal", diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp b/Boards/LilygoTdeck/Source/devices/Display.cpp similarity index 78% rename from Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp rename to Boards/LilygoTdeck/Source/devices/Display.cpp index 7b9e5fbd..ef04affb 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp +++ b/Boards/LilygoTdeck/Source/devices/Display.cpp @@ -1,13 +1,15 @@ -#include "TdeckDisplay.h" -#include "TdeckDisplayConstants.h" +#include "Display.h" #include #include #include -#include - -#define TAG "tdeck_display" +#define TDECK_LCD_SPI_HOST SPI2_HOST +#define TDECK_LCD_PIN_CS GPIO_NUM_12 +#define TDECK_LCD_PIN_DC GPIO_NUM_11 // RS +#define TDECK_LCD_HORIZONTAL_RESOLUTION 320 +#define TDECK_LCD_VERTICAL_RESOLUTION 240 +#define TDECK_LCD_SPI_TRANSFER_HEIGHT (TDECK_LCD_VERTICAL_RESOLUTION / 10) static std::shared_ptr createTouch() { // Note for future changes: Reset pin is 48 and interrupt pin is 47 diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h b/Boards/LilygoTdeck/Source/devices/Display.h similarity index 62% rename from Boards/LilygoTdeck/Source/hal/TdeckDisplay.h rename to Boards/LilygoTdeck/Source/devices/Display.h index 5a0d81b3..7a9b967d 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h +++ b/Boards/LilygoTdeck/Source/devices/Display.h @@ -1,5 +1,5 @@ #pragma once -#include "Tactility/hal/display/DisplayDevice.h" +#include std::shared_ptr createDisplay(); diff --git a/Boards/LilygoTdeck/Source/devices/Power.cpp b/Boards/LilygoTdeck/Source/devices/Power.cpp new file mode 100644 index 00000000..115a11c6 --- /dev/null +++ b/Boards/LilygoTdeck/Source/devices/Power.cpp @@ -0,0 +1,10 @@ +#include +#include + +std::shared_ptr createPower() { + ChargeFromAdcVoltage::Configuration configuration; + // 2.0 ratio, but +.11 added as display voltage sag compensation. + configuration.adcMultiplier = 2.11; + + return std::make_shared(configuration); +} diff --git a/Boards/LilygoTdeck/Source/devices/Power.h b/Boards/LilygoTdeck/Source/devices/Power.h new file mode 100644 index 00000000..e2f20d5f --- /dev/null +++ b/Boards/LilygoTdeck/Source/devices/Power.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::shared_ptr createPower(); diff --git a/Boards/LilygoTdeck/Source/hal/TdeckSdCard.cpp b/Boards/LilygoTdeck/Source/devices/Sdcard.cpp similarity index 87% rename from Boards/LilygoTdeck/Source/hal/TdeckSdCard.cpp rename to Boards/LilygoTdeck/Source/devices/Sdcard.cpp index f4f3119e..ce9a9d53 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckSdCard.cpp +++ b/Boards/LilygoTdeck/Source/devices/Sdcard.cpp @@ -1,17 +1,15 @@ -#include "TdeckSdCard.h" +#include "Sdcard.h" #include #include -#include - using tt::hal::sdcard::SpiSdCardDevice; constexpr auto TDECK_SDCARD_PIN_CS = GPIO_NUM_39; constexpr auto TDECK_LCD_PIN_CS = GPIO_NUM_12; constexpr auto TDECK_RADIO_PIN_CS = GPIO_NUM_9; -std::shared_ptr createTdeckSdCard() { +std::shared_ptr createSdCard() { auto configuration = std::make_unique( TDECK_SDCARD_PIN_CS, GPIO_NUM_NC, diff --git a/Boards/LilygoTdeck/Source/hal/TdeckSdCard.h b/Boards/LilygoTdeck/Source/devices/Sdcard.h similarity index 66% rename from Boards/LilygoTdeck/Source/hal/TdeckSdCard.h rename to Boards/LilygoTdeck/Source/devices/Sdcard.h index e12eb6d0..5cb65a73 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckSdCard.h +++ b/Boards/LilygoTdeck/Source/devices/Sdcard.h @@ -4,4 +4,4 @@ using tt::hal::sdcard::SdCardDevice; -std::shared_ptr createTdeckSdCard(); +std::shared_ptr createSdCard(); diff --git a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp b/Boards/LilygoTdeck/Source/devices/TdeckKeyboard.cpp similarity index 88% rename from Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp rename to Boards/LilygoTdeck/Source/devices/TdeckKeyboard.cpp index 0c357d82..92afdfee 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp +++ b/Boards/LilygoTdeck/Source/devices/TdeckKeyboard.cpp @@ -2,10 +2,9 @@ #include #include -#define TAG "tdeck_keyboard" - -#define TDECK_KEYBOARD_I2C_BUS_HANDLE I2C_NUM_0 -#define TDECK_KEYBOARD_SLAVE_ADDRESS 0x55 +constexpr auto* TAG = "TdeckKeyboard"; +constexpr auto TDECK_KEYBOARD_I2C_BUS_HANDLE = I2C_NUM_0; +constexpr auto TDECK_KEYBOARD_SLAVE_ADDRESS = 0x55; static bool keyboard_i2c_read(uint8_t* output) { return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, 100 / portTICK_PERIOD_MS); @@ -61,7 +60,3 @@ bool TdeckKeyboard::stopLvgl() { bool TdeckKeyboard::isAttached() const { return tt::hal::i2c::masterHasDeviceAtAddress(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, 100); } - -std::shared_ptr createKeyboard() { - return std::make_shared(); -} diff --git a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h b/Boards/LilygoTdeck/Source/devices/TdeckKeyboard.h similarity index 80% rename from Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h rename to Boards/LilygoTdeck/Source/devices/TdeckKeyboard.h index e526d2dc..c79b54dd 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.h +++ b/Boards/LilygoTdeck/Source/devices/TdeckKeyboard.h @@ -2,8 +2,6 @@ #include #include -#include -#include class TdeckKeyboard final : public tt::hal::keyboard::KeyboardDevice { @@ -19,5 +17,3 @@ public: bool isAttached() const override; lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; } }; - -std::shared_ptr createKeyboard(); diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplayConstants.h b/Boards/LilygoTdeck/Source/hal/TdeckDisplayConstants.h deleted file mode 100644 index f0dbb645..00000000 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplayConstants.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#define TDECK_LCD_SPI_HOST SPI2_HOST -#define TDECK_LCD_PIN_CS GPIO_NUM_12 -#define TDECK_LCD_PIN_DC GPIO_NUM_11 // RS -#define TDECK_LCD_HORIZONTAL_RESOLUTION 320 -#define TDECK_LCD_VERTICAL_RESOLUTION 240 -#define TDECK_LCD_SPI_TRANSFER_HEIGHT (TDECK_LCD_VERTICAL_RESOLUTION / 10) diff --git a/Boards/LilygoTdeck/Source/hal/TdeckPower.cpp b/Boards/LilygoTdeck/Source/hal/TdeckPower.cpp deleted file mode 100644 index 008198db..00000000 --- a/Boards/LilygoTdeck/Source/hal/TdeckPower.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "TdeckPower.h" - -#include - -#define TAG "power" - -/** - * 2.0 ratio, but +.11 added as display voltage sag compensation. - */ -#define ADC_MULTIPLIER 2.11 - -#define ADC_REF_VOLTAGE 3.3f -#define BATTERY_VOLTAGE_MIN 3.2f -#define BATTERY_VOLTAGE_MAX 4.2f - -static adc_oneshot_unit_init_cfg_t adcConfig = { - .unit_id = ADC_UNIT_1, - .clk_src = ADC_RTC_CLK_SRC_DEFAULT, - .ulp_mode = ADC_ULP_MODE_DISABLE, -}; - -static adc_oneshot_chan_cfg_t adcChannelConfig = { - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, -}; - -static uint8_t estimateChargeLevelFromVoltage(uint32_t milliVolt) { - float volts = std::min((float)milliVolt / 1000.f, BATTERY_VOLTAGE_MAX); - float voltage_percentage = (volts - BATTERY_VOLTAGE_MIN) / (BATTERY_VOLTAGE_MAX - BATTERY_VOLTAGE_MIN); - float voltage_factor = std::min(1.0f, voltage_percentage); - auto charge_level = (uint8_t) (voltage_factor * 100.f); - TT_LOG_V(TAG, "mV = %lu, scaled = %.2f, factor = %.2f, result = %d", milliVolt, volts, voltage_factor, charge_level); - return charge_level; -} - -TdeckPower::TdeckPower() { - if (adc_oneshot_new_unit(&adcConfig, &adcHandle) != ESP_OK) { - TT_LOG_E(TAG, "ADC config failed"); - return; - } - - if (adc_oneshot_config_channel(adcHandle, ADC_CHANNEL_3, &adcChannelConfig) != ESP_OK) { - TT_LOG_E(TAG, "ADC channel config failed"); - - adc_oneshot_del_unit(adcHandle); - return; - } -} - -TdeckPower::~TdeckPower() { - if (adcHandle) { - adc_oneshot_del_unit(adcHandle); - } -} - -bool TdeckPower::supportsMetric(MetricType type) const { - switch (type) { - using enum MetricType; - case BatteryVoltage: - case ChargeLevel: - return true; - default: - return false; - } - - return false; // Safety guard for when new enum values are introduced -} - -bool TdeckPower::getMetric(MetricType type, MetricData& data) { - switch (type) { - using enum MetricType; - case BatteryVoltage: - return readBatteryVoltageSampled(data.valueAsUint32); - case ChargeLevel: - if (readBatteryVoltageSampled(data.valueAsUint32)) { - data.valueAsUint32 = estimateChargeLevelFromVoltage(data.valueAsUint32); - return true; - } else { - return false; - } - default: - return false; - } - - return false; // Safety guard for when new enum values are introduced -} - -bool TdeckPower::readBatteryVoltageOnce(uint32_t& output) { - int raw; - if (adc_oneshot_read(adcHandle, ADC_CHANNEL_3, &raw) == ESP_OK) { - output = ADC_MULTIPLIER * ((1000.f * ADC_REF_VOLTAGE) / 4096.f) * (float)raw; - TT_LOG_V(TAG, "Raw = %d, voltage = %lu", raw, output); - return true; - } else { - TT_LOG_E(TAG, "Read failed"); - return false; - } -} - -#define MAX_VOLTAGE_SAMPLES 15 - -bool TdeckPower::readBatteryVoltageSampled(uint32_t& output) { - size_t samples_read = 0; - uint32_t sample_accumulator = 0; - uint32_t sample_read_buffer; - - for (size_t i = 0; i < MAX_VOLTAGE_SAMPLES; ++i) { - if (readBatteryVoltageOnce(sample_read_buffer)) { - sample_accumulator += sample_read_buffer; - samples_read++; - } - } - - if (samples_read > 0) { - output = sample_accumulator / samples_read; - return true; - } else { - return false; - } -} - -static std::shared_ptr power; - -std::shared_ptr tdeck_get_power() { - if (power == nullptr) { - power = std::make_shared(); - } - return power; -} - diff --git a/Boards/LilygoTdeck/Source/hal/TdeckPower.h b/Boards/LilygoTdeck/Source/hal/TdeckPower.h deleted file mode 100644 index 4dc4d75c..00000000 --- a/Boards/LilygoTdeck/Source/hal/TdeckPower.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "Tactility/hal/power/PowerDevice.h" -#include -#include - -using tt::hal::power::PowerDevice; - -class TdeckPower : public PowerDevice { - - adc_oneshot_unit_handle_t adcHandle = nullptr; - -public: - - TdeckPower(); - ~TdeckPower(); - - std::string getName() const final { return "ADC Power Measurement"; } - std::string getDescription() const final { return "Power measurement interface via ADC pin"; } - - bool supportsMetric(MetricType type) const override; - bool getMetric(MetricType type, MetricData& data) override; - -private: - - bool readBatteryVoltageSampled(uint32_t& output); - bool readBatteryVoltageOnce(uint32_t& output); -}; - -std::shared_ptr tdeck_get_power(); diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 73dba9f2..e905405c 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -2,19 +2,18 @@ ## Higher Priority -- Move Display settings from flash to `/data/apps/display/display.properties` -- Expose app::Paths to TactilityC -- Call tt::lvgl::isSyncSet after HAL init and show an error (and crash?) when it is not set. - External app loading: Check the version of Tactility and check ESP target hardware to check for compatibility. -- App packaging -- Create more unit tests for `tactility-core` -- Make a URL handler. Use it for handling local files. Match file types with apps. - Fix Development service: when no SD card is present, the app fails to install. Consider installing to `/data` -- Refactor `PropertiesFile.cpp` to use `tt::file::readLines()` (see TODO in code) -- Localize all apps + Note: Change app install to "transfer file" functionality. We can have a proper install when we have app packaging. +- Make a URL handler. Use it for handling local files. Match file types with apps. + Create some kind of "intent" handler like on Android. + The intent can have an action (e.g. view), a URL and an optional bundle. + The manifest can provide the intent handler +- App packaging ## Lower Priority +- Localize all apps - Support hot-plugging SD card (note: this is not possible if they require the CS pin hack) - Create more unit tests for `tactility` - Explore LVGL9's FreeRTOS functionality diff --git a/Drivers/EstimatedPower/CMakeLists.txt b/Drivers/EstimatedPower/CMakeLists.txt new file mode 100644 index 00000000..6e3b34aa --- /dev/null +++ b/Drivers/EstimatedPower/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS "Source" + INCLUDE_DIRS "Source" + REQUIRES Tactility esp_adc +) diff --git a/Drivers/EstimatedPower/README.md b/Drivers/EstimatedPower/README.md new file mode 100644 index 00000000..bd8cbb1b --- /dev/null +++ b/Drivers/EstimatedPower/README.md @@ -0,0 +1,3 @@ +# EstimatedPower + +Use ADC measurements to read voltage and estimate the available power that is left in the battery. diff --git a/Drivers/EstimatedPower/Source/ChargeFromAdcVoltage.cpp b/Drivers/EstimatedPower/Source/ChargeFromAdcVoltage.cpp new file mode 100644 index 00000000..46dee343 --- /dev/null +++ b/Drivers/EstimatedPower/Source/ChargeFromAdcVoltage.cpp @@ -0,0 +1,67 @@ +#include "ChargeFromAdcVoltage.h" +#include +#include + +constexpr auto TAG = "EstimatePower"; +constexpr auto MAX_VOLTAGE_SAMPLES = 15; + +uint8_t ChargeFromAdcVoltage::estimateChargeLevelFromVoltage(uint32_t milliVolt) const { + const float volts = std::min((float)milliVolt / 1000.f, configuration.batteryVoltageMax); + const float voltage_percentage = (volts - configuration.batteryVoltageMin) / (configuration.batteryVoltageMax - configuration.batteryVoltageMin); + const float voltage_factor = std::min(1.0f, voltage_percentage); + const auto charge_level = (uint8_t) (voltage_factor * 100.f); + TT_LOG_V(TAG, "mV = %lu, scaled = %.2f, factor = %.2f, result = %d", milliVolt, volts, voltage_factor, charge_level); + return charge_level; +} + +ChargeFromAdcVoltage::ChargeFromAdcVoltage(const Configuration& configuration) : configuration(configuration) { + if (adc_oneshot_new_unit(&configuration.adcConfig, &adcHandle) != ESP_OK) { + TT_LOG_E(TAG, "ADC config failed"); + return; + } + + if (adc_oneshot_config_channel(adcHandle, configuration.adcChannel, &configuration.adcChannelConfig) != ESP_OK) { + TT_LOG_E(TAG, "ADC channel config failed"); + + adc_oneshot_del_unit(adcHandle); + return; + } +} + +ChargeFromAdcVoltage::~ChargeFromAdcVoltage() { + if (adcHandle) { + adc_oneshot_del_unit(adcHandle); + } +} + +bool ChargeFromAdcVoltage::readBatteryVoltageOnce(uint32_t& output) const { + int raw; + if (adc_oneshot_read(adcHandle, configuration.adcChannel, &raw) == ESP_OK) { + output = configuration.adcMultiplier * ((1000.f * configuration.adcRefVoltage) / 4096.f) * (float)raw; + TT_LOG_V(TAG, "Raw = %d, voltage = %lu", raw, output); + return true; + } else { + TT_LOG_E(TAG, "Read failed"); + return false; + } +} + +bool ChargeFromAdcVoltage::readBatteryVoltageSampled(uint32_t& output) const { + size_t samples_read = 0; + uint32_t sample_accumulator = 0; + uint32_t sample_read_buffer; + + for (size_t i = 0; i < MAX_VOLTAGE_SAMPLES; ++i) { + if (readBatteryVoltageOnce(sample_read_buffer)) { + sample_accumulator += sample_read_buffer; + samples_read++; + } + } + + if (samples_read == 0) { + return false; + } + + output = sample_accumulator / samples_read; + return true; +} diff --git a/Drivers/EstimatedPower/Source/ChargeFromAdcVoltage.h b/Drivers/EstimatedPower/Source/ChargeFromAdcVoltage.h new file mode 100644 index 00000000..2023a2f3 --- /dev/null +++ b/Drivers/EstimatedPower/Source/ChargeFromAdcVoltage.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +class ChargeFromAdcVoltage { + +public: + + struct Configuration { + adc_channel_t adcChannel = ADC_CHANNEL_3; + float adcMultiplier = 1.0f; + float adcRefVoltage = 3.3f; + float batteryVoltageMin = 3.2f; + float batteryVoltageMax = 4.2f; + adc_oneshot_unit_init_cfg_t adcConfig = { + .unit_id = ADC_UNIT_1, + .clk_src = ADC_RTC_CLK_SRC_DEFAULT, + .ulp_mode = ADC_ULP_MODE_DISABLE, + }; + adc_oneshot_chan_cfg_t adcChannelConfig = { + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + }; + +private: + + adc_oneshot_unit_handle_t adcHandle = nullptr; + Configuration configuration; + +public: + + explicit ChargeFromAdcVoltage(const Configuration& configuration); + + ~ChargeFromAdcVoltage(); + + uint8_t estimateChargeLevelFromVoltage(uint32_t milliVolt) const; + + bool readBatteryVoltageSampled(uint32_t& output) const; + + bool readBatteryVoltageOnce(uint32_t& output) const; +}; diff --git a/Drivers/EstimatedPower/Source/EstimatedPower.cpp b/Drivers/EstimatedPower/Source/EstimatedPower.cpp new file mode 100644 index 00000000..93f43cb8 --- /dev/null +++ b/Drivers/EstimatedPower/Source/EstimatedPower.cpp @@ -0,0 +1,30 @@ +#include "EstimatedPower.h" + +bool EstimatedPower::supportsMetric(MetricType type) const { + switch (type) { + using enum MetricType; + case BatteryVoltage: + case ChargeLevel: + return true; + default: + return false; + } +} + +bool EstimatedPower::getMetric(MetricType type, MetricData& data) { + switch (type) { + using enum MetricType; + case BatteryVoltage: + return chargeFromAdcVoltage->readBatteryVoltageSampled(data.valueAsUint32); + case ChargeLevel: + if (chargeFromAdcVoltage->readBatteryVoltageSampled(data.valueAsUint32)) { + data.valueAsUint32 = chargeFromAdcVoltage->estimateChargeLevelFromVoltage(data.valueAsUint32); + return true; + } else { + return false; + } + default: + return false; + } +} + diff --git a/Drivers/EstimatedPower/Source/EstimatedPower.h b/Drivers/EstimatedPower/Source/EstimatedPower.h new file mode 100644 index 00000000..ec2a6a00 --- /dev/null +++ b/Drivers/EstimatedPower/Source/EstimatedPower.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +using tt::hal::power::PowerDevice; + +/** + * Uses Voltage measurements to estimate charge. + * Supports voltage and charge level metrics. + * Can be overridden to further extend supported metrics. + */ +class EstimatedPower final : public PowerDevice { + + std::unique_ptr chargeFromAdcVoltage; + +public: + + EstimatedPower(ChargeFromAdcVoltage::Configuration configuration) : + chargeFromAdcVoltage(std::make_unique(std::move(configuration))) {} + + std::string getName() const override { return "ADC Power Measurement"; } + std::string getDescription() const override { return "Power measurement interface via ADC pin"; } + + bool supportsMetric(MetricType type) const override; + bool getMetric(MetricType type, MetricData& data) override; +}; diff --git a/Tactility/Include/Tactility/Tactility.h b/Tactility/Include/Tactility/Tactility.h index f90e8bc5..01ac0d41 100644 --- a/Tactility/Include/Tactility/Tactility.h +++ b/Tactility/Include/Tactility/Tactility.h @@ -1,13 +1,13 @@ #pragma once -#include "Tactility/app/AppManifest.h" - +#include +#include #include #include namespace tt { -namespace app::launcher { extern const app::AppManifest manifest; } +namespace app::launcher { extern const AppManifest manifest; } /** @brief The configuration for the operating system * It contains the hardware configuration, apps and services @@ -33,4 +33,7 @@ void run(const Configuration& config); */ const Configuration* _Nullable getConfiguration(); + +Dispatcher& getMainDispatcher(); + } // namespace diff --git a/Tactility/Include/Tactility/hal/Configuration.h b/Tactility/Include/Tactility/hal/Configuration.h index 0d97f86d..e3eded79 100644 --- a/Tactility/Include/Tactility/hal/Configuration.h +++ b/Tactility/Include/Tactility/hal/Configuration.h @@ -1,8 +1,8 @@ #pragma once -#include "Tactility/hal/sdcard/SdCardDevice.h" -#include "Tactility/hal/spi/Spi.h" -#include "Tactility/hal/uart/Uart.h" +#include +#include +#include #include "i2c/I2c.h" namespace tt::hal { diff --git a/Tactility/Include/Tactility/service/gps/GpsService.h b/Tactility/Include/Tactility/service/gps/GpsService.h index de213e21..64e37f53 100644 --- a/Tactility/Include/Tactility/service/gps/GpsService.h +++ b/Tactility/Include/Tactility/service/gps/GpsService.h @@ -11,8 +11,6 @@ namespace tt::service::gps { class GpsService final : public Service { -private: - struct GpsDeviceRecord { std::shared_ptr device = nullptr; hal::gps::GpsDevice::GgaSubscriptionId satelliteSubscriptionId = -1; @@ -25,7 +23,7 @@ private: Mutex mutex = Mutex(Mutex::Type::Recursive); Mutex stateMutex; std::vector deviceRecords; - std::shared_ptr statePubSub = std::make_shared(); + std::shared_ptr> statePubSub = std::make_shared>(); std::unique_ptr paths; State state = State::Off; @@ -46,8 +44,8 @@ private: public: - void onStart(tt::service::ServiceContext &serviceContext) final; - void onStop(tt::service::ServiceContext &serviceContext) final; + void onStart(ServiceContext &serviceContext) override; + void onStop(ServiceContext &serviceContext) override; bool addGpsConfiguration(hal::gps::GpsConfiguration configuration); bool removeGpsConfiguration(hal::gps::GpsConfiguration configuration); @@ -61,7 +59,7 @@ public: bool getCoordinates(minmea_sentence_rmc& rmc) const; /** @return GPS service pubsub that broadcasts State* objects */ - std::shared_ptr getStatePubsub() const { return statePubSub; } + std::shared_ptr> getStatePubsub() const { return statePubSub; } }; std::shared_ptr findGpsService(); diff --git a/Tactility/Include/Tactility/service/loader/Loader.h b/Tactility/Include/Tactility/service/loader/Loader.h index d5d3b651..6db1bdf3 100644 --- a/Tactility/Include/Tactility/service/loader/Loader.h +++ b/Tactility/Include/Tactility/service/loader/Loader.h @@ -11,15 +11,11 @@ namespace tt::service::loader { // region LoaderEvent for PubSub -typedef enum { - LoaderEventTypeApplicationStarted, - LoaderEventTypeApplicationShowing, - LoaderEventTypeApplicationHiding, - LoaderEventTypeApplicationStopped -} LoaderEventType; - -struct LoaderEvent { - LoaderEventType type; +enum class LoaderEvent{ + ApplicationStarted, + ApplicationShowing, + ApplicationHiding, + ApplicationStopped }; // endregion LoaderEvent for PubSub @@ -43,6 +39,6 @@ std::shared_ptr _Nullable getCurrentApp(); /** * @brief PubSub for LoaderEvent */ -std::shared_ptr getPubsub(); +std::shared_ptr> getPubsub(); } // namespace diff --git a/Tactility/Include/Tactility/service/wifi/Wifi.h b/Tactility/Include/Tactility/service/wifi/Wifi.h index 88cd3d6a..99a023f6 100644 --- a/Tactility/Include/Tactility/service/wifi/Wifi.h +++ b/Tactility/Include/Tactility/service/wifi/Wifi.h @@ -33,7 +33,7 @@ typedef enum { namespace tt::service::wifi { -enum class EventType { +enum class WifiEvent { /** Radio was turned on */ RadioStateOn, /** Radio is turning on. */ @@ -61,10 +61,6 @@ enum class RadioState { Off, }; -struct Event { - EventType type; -}; - struct ApRecord { std::string ssid; int8_t rssi; @@ -76,7 +72,7 @@ struct ApRecord { * @brief Get wifi pubsub that broadcasts Event objects * @return PubSub */ -std::shared_ptr getPubsub(); +std::shared_ptr> getPubsub(); /** @return Get the current radio state */ RadioState getRadioState(); diff --git a/Tactility/Private/Tactility/app/wificonnect/WifiConnect.h b/Tactility/Private/Tactility/app/wificonnect/WifiConnect.h index bbb17cc5..5f4eea00 100644 --- a/Tactility/Private/Tactility/app/wificonnect/WifiConnect.h +++ b/Tactility/Private/Tactility/app/wificonnect/WifiConnect.h @@ -19,9 +19,11 @@ class WifiConnect : public App { .onConnectSsidContext = nullptr }; View view = View(&bindings, &state); - PubSub::SubscriptionHandle wifiSubscription; + PubSub::SubscriptionHandle wifiSubscription; bool view_enabled = false; + void onWifiEvent(service::wifi::WifiEvent event); + public: WifiConnect(); diff --git a/Tactility/Private/Tactility/app/wifimanage/WifiManagePrivate.h b/Tactility/Private/Tactility/app/wifimanage/WifiManagePrivate.h index 9b6b1013..8d0f56d9 100644 --- a/Tactility/Private/Tactility/app/wifimanage/WifiManagePrivate.h +++ b/Tactility/Private/Tactility/app/wifimanage/WifiManagePrivate.h @@ -13,15 +13,15 @@ namespace tt::app::wifimanage { class WifiManage : public App { -private: - - PubSub::SubscriptionHandle wifiSubscription = nullptr; + PubSub::SubscriptionHandle wifiSubscription = nullptr; Mutex mutex; Bindings bindings = { }; State state; View view = View(&bindings, &state); bool isViewEnabled = false; + void onWifiEvent(service::wifi::WifiEvent event); + public: WifiManage(); diff --git a/Tactility/Private/Tactility/hal/Hal_i.h b/Tactility/Private/Tactility/hal/HalPrivate.h similarity index 100% rename from Tactility/Private/Tactility/hal/Hal_i.h rename to Tactility/Private/Tactility/hal/HalPrivate.h diff --git a/Tactility/Private/Tactility/service/gui/GuiService.h b/Tactility/Private/Tactility/service/gui/GuiService.h index addec094..89729f29 100644 --- a/Tactility/Private/Tactility/service/gui/GuiService.h +++ b/Tactility/Private/Tactility/service/gui/GuiService.h @@ -1,14 +1,13 @@ #pragma once +#include #include #include #include #include - -#include "Tactility/app/AppContext.h" +#include #include - #include namespace tt::service::gui { @@ -23,7 +22,7 @@ class GuiService : public Service { // Thread and lock Thread* thread = nullptr; Mutex mutex = Mutex(Mutex::Type::Recursive); - PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; + PubSub::SubscriptionHandle loader_pubsub_subscription = nullptr; // Layers and Canvas lv_obj_t* appRootWidget = nullptr; @@ -37,10 +36,10 @@ class GuiService : public Service { bool isStarted = false; - static void onLoaderMessage(const void* message, TT_UNUSED void* context); - static int32_t guiMain(); + void onLoaderEvent(loader::LoaderEvent event); + lv_obj_t* createAppViews(lv_obj_t* parent); void redraw(); diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index a2516ae3..735ceb67 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -1,28 +1,46 @@ #include +#include #include -#include -#include +#include +#include +#include #include +#include #include #include #include +#include + +#ifdef ESP_PLATFORM +#include +#endif namespace tt { -#define TAG "Tactility" +constexpr auto* TAG = "Tactility"; static const Configuration* config_instance = nullptr; +static Dispatcher mainDispatcher; // region Default services - namespace service { + // Primary + namespace gps { extern const ServiceManifest manifest; } + namespace wifi { extern const ServiceManifest manifest; } + namespace sdcard { extern const ServiceManifest manifest; } +#ifdef ESP_PLATFORM + namespace development { extern const ServiceManifest manifest; } + namespace espnow { extern const ServiceManifest manifest; } +#endif + // Secondary (UI) namespace gui { extern const ServiceManifest manifest; } namespace loader { extern const ServiceManifest manifest; } namespace statusbar { extern const ServiceManifest manifest; } #if TT_FEATURE_SCREENSHOT_ENABLED namespace screenshot { extern const ServiceManifest manifest; } #endif + } // endregion @@ -127,7 +145,7 @@ static void registerUserApps(const std::vector& apps) { } } -static void registerAndStartSystemServices() { +static void registerAndStartSecondaryServices() { TT_LOG_I(TAG, "Registering and starting system services"); addService(service::loader::manifest); addService(service::gui::manifest); @@ -137,6 +155,17 @@ static void registerAndStartSystemServices() { #endif } +static void registerAndStartPrimaryServices() { + TT_LOG_I(TAG, "Registering and starting system services"); + addService(service::gps::manifest); + addService(service::sdcard::manifest); + addService(service::wifi::manifest); +#ifdef ESP_PLATFORM + addService(service::development::manifest); + addService(service::espnow::manifest); +#endif +} + static void registerAndStartUserServices(const std::vector& manifests) { TT_LOG_I(TAG, "Registering and starting user services"); for (auto* manifest : manifests) { @@ -165,11 +194,18 @@ void run(const Configuration& config) { // Assign early so starting services can use it config_instance = &config; - initHeadless(hardware); + TT_LOG_I(TAG, "Tactility v%s on %s (%s)", TT_VERSION, CONFIG_TT_BOARD_NAME, CONFIG_TT_BOARD_ID); +#ifdef ESP_PLATFORM + initEsp(); +#endif + settings::initTimeZone(); + hal::init(*config.hardware); + hal::sdcard::mountAll(); + network::ntp::init(); + registerAndStartPrimaryServices(); lvgl::init(hardware); - - registerAndStartSystemServices(); + registerAndStartSecondaryServices(); TT_LOG_I(TAG, "starting boot app"); // The boot app takes care of registering system apps, user services and user apps @@ -180,7 +216,7 @@ void run(const Configuration& config) { TT_LOG_I(TAG, "Processing main dispatcher"); while (true) { - getMainDispatcher().consume(); + mainDispatcher.consume(); } } @@ -188,4 +224,8 @@ const Configuration* _Nullable getConfiguration() { return config_instance; } +Dispatcher& getMainDispatcher() { + return mainDispatcher; +} + } // namespace diff --git a/Tactility/Source/TactilityHeadless.cpp b/Tactility/Source/TactilityHeadless.cpp deleted file mode 100644 index a74ff2bf..00000000 --- a/Tactility/Source/TactilityHeadless.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "Tactility/TactilityHeadless.h" -#include "Tactility/hal/Configuration.h" -#include "Tactility/hal/Hal_i.h" -#include "Tactility/network/NtpPrivate.h" -#include "Tactility/service/ServiceManifest.h" -#include "Tactility/service/ServiceRegistration.h" - -#include -#include -#include - -#ifdef ESP_PLATFORM -#include "Tactility/InitEsp.h" -#endif - -namespace tt { - -constexpr auto* TAG = "Tactility"; - -namespace service::gps { extern const ServiceManifest manifest; } -namespace service::wifi { extern const ServiceManifest manifest; } -namespace service::sdcard { extern const ServiceManifest manifest; } -#ifdef ESP_PLATFORM -namespace service::development { extern const ServiceManifest manifest; } -namespace service::espnow { extern const ServiceManifest manifest; } -#endif - -static Dispatcher mainDispatcher; - -static const hal::Configuration* hardwareConfig = nullptr; - -static void registerAndStartSystemServices() { - TT_LOG_I(TAG, "Registering and starting system services"); - addService(service::gps::manifest); - addService(service::sdcard::manifest); - addService(service::wifi::manifest); -#ifdef ESP_PLATFORM - addService(service::development::manifest); - addService(service::espnow::manifest); -#endif -} - -void initHeadless(const hal::Configuration& config) { - TT_LOG_I(TAG, "Tactility v%s on %s (%s)", TT_VERSION, CONFIG_TT_BOARD_NAME, CONFIG_TT_BOARD_ID); -#ifdef ESP_PLATFORM - initEsp(); -#endif - hardwareConfig = &config; - settings::initTimeZone(); - hal::init(config); - hal::sdcard::mountAll(); - network::ntp::init(); - registerAndStartSystemServices(); -} - -Dispatcher& getMainDispatcher() { - return mainDispatcher; -} - -namespace hal { - -const Configuration* getConfiguration() { - return hardwareConfig; -} - -} // namespace hal - -} // namespace tt diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index fc8801dc..65f0d331 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -32,7 +32,7 @@ class BootApp : public App { Thread thread = Thread( "boot", - 4096, + 5120, [] { return bootThreadCallback(); }, getCpuAffinityConfiguration().system ); diff --git a/Tactility/Source/app/development/Development.cpp b/Tactility/Source/app/development/Development.cpp index 31564108..13a1ea26 100644 --- a/Tactility/Source/app/development/Development.cpp +++ b/Tactility/Source/app/development/Development.cpp @@ -1,17 +1,19 @@ #ifdef ESP_PLATFORM +#include + #include #include #include #include #include +#include #include #include #include #include #include -#include namespace tt::app::development { @@ -51,7 +53,10 @@ class DevelopmentApp final : public App { bool is_on = lv_obj_has_state(widget, LV_STATE_CHECKED); bool is_changed = is_on != service::development::shouldEnableOnBoot(); if (is_changed) { - service::development::setEnableOnBoot(is_on); + // Dispatch it, so file IO doesn't block the UI + getMainDispatcher().dispatch([is_on] { + service::development::setEnableOnBoot(is_on); + }); } } } diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index 7d93b5ed..b853f80c 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -1,8 +1,9 @@ +#include + #include #include #include #include -#include #include @@ -139,7 +140,11 @@ public: void onHide(TT_UNUSED AppContext& app) override { if (displaySettingsUpdated) { - settings::display::save(displaySettings); + // Dispatch it, so file IO doesn't block the UI + const settings::display::DisplaySettings settings_to_save = displaySettings; + getMainDispatcher().dispatch([settings_to_save] { + settings::display::save(settings_to_save); + }); } } }; diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index c80de746..250c87be 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -1,12 +1,13 @@ -#include "Tactility/TactilityHeadless.h" -#include "Tactility/Timer.h" -#include "Tactility/app/AppManifest.h" -#include "Tactility/app/alertdialog/AlertDialog.h" -#include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Toolbar.h" -#include "Tactility/service/gps/GpsUtil.h" -#include "Tactility/service/loader/Loader.h" +#include + +#include +#include +#include +#include #include +#include +#include +#include #include #include @@ -34,14 +35,9 @@ class GpsSettingsApp final : public App { lv_obj_t* gpsConfigWrapper = nullptr; lv_obj_t* addGpsWrapper = nullptr; bool hasSetInfo = false; - PubSub::SubscriptionHandle serviceStateSubscription = nullptr; + PubSub::SubscriptionHandle serviceStateSubscription = nullptr; std::shared_ptr service; - static void onServiceStateChangedCallback(const void* data, void* context) { - auto* app = (GpsSettingsApp*)context; - app->onServiceStateChanged(); - } - void onServiceStateChanged() { auto lock = lvgl::getSyncLock()->asScopedLock(); if (lock.lock(100 / portTICK_PERIOD_MS)) { @@ -313,7 +309,9 @@ public: lv_obj_set_style_pad_all(infoContainerWidget, 0, 0); hasSetInfo = false; - serviceStateSubscription = service->getStatePubsub()->subscribe(onServiceStateChangedCallback, this); + serviceStateSubscription = service->getStatePubsub()->subscribe([this](auto) { + onServiceStateChanged(); + }); gpsConfigWrapper = lv_obj_create(main_wrapper); lv_obj_set_size(gpsConfigWrapper, LV_PCT(100), LV_SIZE_CONTENT); diff --git a/Tactility/Source/app/screenshot/Screenshot.cpp b/Tactility/Source/app/screenshot/Screenshot.cpp index 3844f82b..f19c8b30 100644 --- a/Tactility/Source/app/screenshot/Screenshot.cpp +++ b/Tactility/Source/app/screenshot/Screenshot.cpp @@ -1,19 +1,18 @@ -#include "Tactility/TactilityConfig.h" +#include +#include #include #include #if TT_FEATURE_SCREENSHOT_ENABLED -#include "Tactility/app/App.h" -#include "Tactility/app/AppManifest.h" -#include "Tactility/lvgl/LvglSync.h" -#include "Tactility/lvgl/Toolbar.h" -#include "Tactility/service/screenshot/Screenshot.h" +#include +#include +#include +#include +#include -#include - -#define TAG "screenshot" +constexpr auto* TAG = "Screenshot"; namespace tt::app::screenshot { diff --git a/Tactility/Source/app/wificonnect/State.cpp b/Tactility/Source/app/wificonnect/State.cpp index c32da4b0..7b9605d7 100644 --- a/Tactility/Source/app/wificonnect/State.cpp +++ b/Tactility/Source/app/wificonnect/State.cpp @@ -1,6 +1,4 @@ -#include "Tactility/app/wificonnect/State.h" - -#include +#include namespace tt::app::wificonnect { diff --git a/Tactility/Source/app/wificonnect/View.cpp b/Tactility/Source/app/wificonnect/View.cpp index 8d977806..6b3e5616 100644 --- a/Tactility/Source/app/wificonnect/View.cpp +++ b/Tactility/Source/app/wificonnect/View.cpp @@ -1,19 +1,18 @@ -#include "Tactility/app/wificonnect/View.h" -#include "Tactility/app/wificonnect/WifiConnect.h" - -#include "Tactility/lvgl/Toolbar.h" -#include "Tactility/lvgl/Spinner.h" - #include -#include -#include +#include +#include +#include +#include #include #include +#include +#include + namespace tt::app::wificonnect { -#define TAG "wifi_connect" +constexpr auto* TAG = "WifiConnect"; void View::resetErrors() { lv_obj_add_flag(password_error, LV_OBJ_FLAG_HIDDEN); diff --git a/Tactility/Source/app/wificonnect/WifiConnect.cpp b/Tactility/Source/app/wificonnect/WifiConnect.cpp index 459714ed..e8be8b8e 100644 --- a/Tactility/Source/app/wificonnect/WifiConnect.cpp +++ b/Tactility/Source/app/wificonnect/WifiConnect.cpp @@ -1,41 +1,18 @@ -#include "Tactility/app/wificonnect/WifiConnect.h" +#include -#include "Tactility/app/AppContext.h" -#include "Tactility/service/loader/Loader.h" -#include "Tactility/service/wifi/Wifi.h" -#include "Tactility/lvgl/LvglSync.h" +#include +#include +#include +#include namespace tt::app::wificonnect { -#define TAG "wifi_connect" -#define WIFI_CONNECT_PARAM_SSID "ssid" // String -#define WIFI_CONNECT_PARAM_PASSWORD "password" // String + +constexpr auto* TAG = "WifiConnect"; +constexpr auto* WIFI_CONNECT_PARAM_SSID = "ssid"; // String +constexpr auto* WIFI_CONNECT_PARAM_PASSWORD = "password"; // String extern const AppManifest manifest; -static void eventCallback(const void* message, void* context) { - auto* event = static_cast(message); - auto* wifi = static_cast(context); - State& state = wifi->getState(); - switch (event->type) { - case service::wifi::EventType::ConnectionFailed: - if (state.isConnecting()) { - state.setConnecting(false); - state.setConnectionError(true); - wifi->requestViewUpdate(); - } - break; - case service::wifi::EventType::ConnectionSuccess: - if (wifi->getState().isConnecting()) { - state.setConnecting(false); - service::loader::stopApp(); - } - break; - default: - break; - } - wifi->requestViewUpdate(); -} - static void onConnect(const service::wifi::settings::WifiApSettings& ap_settings, bool remember, TT_UNUSED void* parameter) { auto* wifi = static_cast(parameter); wifi->getState().setApSettings(ap_settings); @@ -43,8 +20,33 @@ static void onConnect(const service::wifi::settings::WifiApSettings& ap_settings service::wifi::connect(ap_settings, remember); } +void WifiConnect::onWifiEvent(service::wifi::WifiEvent event) { + State& state = getState(); + switch (event) { + case service::wifi::WifiEvent::ConnectionFailed: + if (state.isConnecting()) { + state.setConnecting(false); + state.setConnectionError(true); + requestViewUpdate(); + } + break; + case service::wifi::WifiEvent::ConnectionSuccess: + if (getState().isConnecting()) { + state.setConnecting(false); + service::loader::stopApp(); + } + break; + default: + break; + } + requestViewUpdate(); +} + WifiConnect::WifiConnect() { - wifiSubscription = service::wifi::getPubsub()->subscribe(&eventCallback, this); + wifiSubscription = service::wifi::getPubsub()->subscribe([this](auto event) { + onWifiEvent(event); + }); + bindings = (Bindings) { .onConnectSsid = onConnect, .onConnectSsidContext = this, diff --git a/Tactility/Source/app/wifimanage/State.cpp b/Tactility/Source/app/wifimanage/State.cpp index d69d8a00..084a4d84 100644 --- a/Tactility/Source/app/wifimanage/State.cpp +++ b/Tactility/Source/app/wifimanage/State.cpp @@ -1,4 +1,4 @@ -#include "Tactility/app/wifimanage/WifiManagePrivate.h" +#include namespace tt::app::wifimanage { diff --git a/Tactility/Source/app/wifimanage/View.cpp b/Tactility/Source/app/wifimanage/View.cpp index 2bb355c0..6b2aa9a6 100644 --- a/Tactility/Source/app/wifimanage/View.cpp +++ b/Tactility/Source/app/wifimanage/View.cpp @@ -1,9 +1,11 @@ -#include "Tactility/app/wifimanage/View.h" -#include "Tactility/app/wifimanage/WifiManagePrivate.h" +#include -#include "Tactility/lvgl/Style.h" -#include "Tactility/lvgl/Toolbar.h" -#include "Tactility/lvgl/Spinner.h" +#include +#include + +#include +#include +#include #include #include @@ -15,7 +17,7 @@ namespace tt::app::wifimanage { -#define TAG "wifi_main_view" +constexpr auto* TAG = "WifiManageView"; std::shared_ptr _Nullable optWifiManage(); @@ -49,7 +51,10 @@ static void on_enable_on_boot_switch_changed(lv_event_t* event) { auto* enable_switch = static_cast(lv_event_get_target(event)); if (code == LV_EVENT_VALUE_CHANGED) { bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); - service::wifi::settings::setEnableOnBoot(is_on); + // Dispatch it, so file IO doesn't block the UI + getMainDispatcher().dispatch([is_on] { + service::wifi::settings::setEnableOnBoot(is_on); + }); } } diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index 827250a4..6e4f32b3 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -1,16 +1,15 @@ -#include "Tactility/app/wifimanage/WifiManagePrivate.h" -#include "Tactility/app/wifimanage/View.h" +#include +#include -#include "Tactility/app/AppContext.h" -#include "Tactility/app/wifiapsettings/WifiApSettings.h" -#include "Tactility/service/loader/Loader.h" -#include "Tactility/service/wifi/WifiSettings.h" -#include "Tactility/lvgl/LvglSync.h" -#include "Tactility/app/wificonnect/WifiConnect.h" +#include +#include +#include +#include +#include namespace tt::app::wifimanage { -#define TAG "wifi_manage" +constexpr auto TAG = "WifiManage"; extern const AppManifest manifest; @@ -72,20 +71,18 @@ void WifiManage::requestViewUpdate() { unlock(); } -static void wifiManageEventCallback(const void* message, void* context) { - auto* event = (service::wifi::Event*)message; - auto* wifi = (WifiManage*)context; +void WifiManage::onWifiEvent(service::wifi::WifiEvent event) { auto radio_state = service::wifi::getRadioState(); TT_LOG_I(TAG, "Update with state %s", service::wifi::radioStateToString(radio_state)); - wifi->getState().setRadioState(radio_state); - switch (event->type) { - using enum tt::service::wifi::EventType; + getState().setRadioState(radio_state); + switch (event) { + using enum service::wifi::WifiEvent; case ScanStarted: - wifi->getState().setScanning(true); + getState().setScanning(true); break; case ScanFinished: - wifi->getState().setScanning(false); - wifi->getState().updateApRecords(); + getState().setScanning(false); + getState().updateApRecords(); break; case RadioStateOn: if (!service::wifi::isScanning()) { @@ -96,11 +93,13 @@ static void wifiManageEventCallback(const void* message, void* context) { break; } - wifi->requestViewUpdate(); + requestViewUpdate(); } void WifiManage::onShow(AppContext& app, lv_obj_t* parent) { - wifiSubscription = service::wifi::getPubsub()->subscribe(&wifiManageEventCallback, this); + wifiSubscription = service::wifi::getPubsub()->subscribe([this](auto event) { + onWifiEvent(event); + }); // State update (it has its own locking) state.setRadioState(service::wifi::getRadioState()); diff --git a/Tactility/Source/file/PropertiesFile.cpp b/Tactility/Source/file/PropertiesFile.cpp index 088f44d8..9a7a549d 100644 --- a/Tactility/Source/file/PropertiesFile.cpp +++ b/Tactility/Source/file/PropertiesFile.cpp @@ -21,31 +21,21 @@ bool getKeyValuePair(const std::string& input, std::string& key, std::string& va bool loadPropertiesFile(const std::string& filePath, std::function callback) { return file::withLock(filePath, [&filePath, &callback] { TT_LOG_I(TAG, "Reading properties file %s", filePath.c_str()); - const auto input = readString(filePath); - if (input == nullptr) { - TT_LOG_E(TAG, "Failed to read file contents of %s", filePath.c_str()); - return false; - } - - const auto* input_start = reinterpret_cast(input.get()); - const std::string input_string = input_start; - uint16_t line_count = 0; - // TODO: Rewrite to use file::readLines() - string::split(input_string, "\n", [&line_count, &filePath, &callback](auto token) { + return readLines(filePath, true, [&line_count, &filePath, &callback](const std::string& line) { line_count++; std::string key, value; - auto trimmed_token = string::trim(token, " \t"); - if (!trimmed_token.starts_with("#")) { - if (getKeyValuePair(token, key, value)) { + auto trimmed_line = string::trim(line, " \t"); + if (!trimmed_line.starts_with("#")) { + if (getKeyValuePair(trimmed_line, key, value)) { std::string trimmed_key = string::trim(key, " \t"); std::string trimmed_value = string::trim(value, " \t"); callback(trimmed_key, trimmed_value); - } else { TT_LOG_E(TAG, "Failed to parse line %d of %s", line_count, filePath.c_str()); } + } else { + TT_LOG_E(TAG, "Failed to parse line %d of %s", line_count, filePath.c_str()); + } } }); - - return true; }); } diff --git a/Tactility/Source/hal/Hal.cpp b/Tactility/Source/hal/Hal.cpp index f46f00d0..e1d9355b 100644 --- a/Tactility/Source/hal/Hal.cpp +++ b/Tactility/Source/hal/Hal.cpp @@ -1,3 +1,4 @@ +#include "Tactility/Tactility.h" #include "Tactility/hal/Configuration.h" #include "Tactility/hal/Device.h" #include "Tactility/hal/gps/GpsInit.h" @@ -10,7 +11,7 @@ namespace tt::hal { -constexpr auto* TAG = "hal"; +constexpr auto* TAG = "Hal"; void registerDevices(const Configuration& configuration) { TT_LOG_I(TAG, "Registering devices"); @@ -62,4 +63,8 @@ void init(const Configuration& configuration) { kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd); } +const Configuration* getConfiguration() { + return tt::getConfiguration()->hardware; +} + } // namespace diff --git a/Tactility/Source/hal/uart/Uart.cpp b/Tactility/Source/hal/uart/Uart.cpp index 1c8681b8..6f1df0f8 100644 --- a/Tactility/Source/hal/uart/Uart.cpp +++ b/Tactility/Source/hal/uart/Uart.cpp @@ -5,13 +5,13 @@ #include #include +#include #ifdef ESP_PLATFORM -#include "Tactility/TactilityHeadless.h" -#include "Tactility/hal/uart/UartEsp.h" +#include #include #else -#include "Tactility/hal/uart/UartPosix.h" +#include #include #endif @@ -29,7 +29,7 @@ struct UartEntry { static std::vector uartEntries = {}; static uint32_t lastUartId = uartIdNotInUse; -bool init(const std::vector& configurations) { +bool init(const std::vector& configurations) { TT_LOG_I(TAG, "Init"); for (const auto& configuration: configurations) { uartEntries.push_back({ @@ -142,7 +142,7 @@ void close(uint32_t uartId) { std::vector getNames() { std::vector names; #ifdef ESP_PLATFORM - for (auto& config : getConfiguration()->uart) { + for (auto& config : hal::getConfiguration()->uart) { names.push_back(config.name); } #else diff --git a/Tactility/Source/hal/usb/Usb.cpp b/Tactility/Source/hal/usb/Usb.cpp index 009507dd..e094f777 100644 --- a/Tactility/Source/hal/usb/Usb.cpp +++ b/Tactility/Source/hal/usb/Usb.cpp @@ -1,17 +1,15 @@ #ifdef ESP_PLATFORM -#include "Tactility/hal/usb/Usb.h" -#include "Tactility/TactilityHeadless.h" -#include "Tactility/hal/sdcard/SpiSdCardDevice.h" -#include "Tactility/hal/usb/UsbTusb.h" +#include +#include +#include #include namespace tt::hal::usb { -#define TAG "usb" - -#define BOOT_FLAG 42 +constexpr auto* TAG = "usb"; +constexpr auto BOOT_FLAG = 42; struct BootMode { uint32_t flag = 0; diff --git a/Tactility/Source/lvgl/Lvgl.cpp b/Tactility/Source/lvgl/Lvgl.cpp index 1cd9b38d..c280cb10 100644 --- a/Tactility/Source/lvgl/Lvgl.cpp +++ b/Tactility/Source/lvgl/Lvgl.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #ifdef ESP_PLATFORM #include diff --git a/Tactility/Source/lvgl/Statusbar.cpp b/Tactility/Source/lvgl/Statusbar.cpp index 0e27a870..b90fa90d 100644 --- a/Tactility/Source/lvgl/Statusbar.cpp +++ b/Tactility/Source/lvgl/Statusbar.cpp @@ -28,9 +28,9 @@ struct StatusbarIcon { struct StatusbarData { Mutex mutex = Mutex(Mutex::Type::Recursive); - std::shared_ptr pubsub = std::make_shared(); + std::shared_ptr> pubsub = std::make_shared>(); StatusbarIcon icons[STATUSBAR_ICON_LIMIT] = {}; - Timer* time_update_timer = new Timer(Timer::Type::Once, []() { onUpdateTime(); }); + Timer* time_update_timer = new Timer(Timer::Type::Once, [] { onUpdateTime(); }); uint8_t time_hours = 0; uint8_t time_minutes = 0; bool time_set = false; @@ -44,7 +44,7 @@ typedef struct { lv_obj_t* time; lv_obj_t* icons[STATUSBAR_ICON_LIMIT]; lv_obj_t* battery_icon; - PubSub::SubscriptionHandle pubsub_subscription; + PubSub::SubscriptionHandle pubsub_subscription; } Statusbar; static bool statusbar_lock(TickType_t timeoutTicks = portMAX_DELAY) { @@ -108,9 +108,8 @@ static const lv_obj_class_t statusbar_class = { .theme_inheritable = false }; -static void statusbar_pubsub_event(TT_UNUSED const void* message, void* obj) { - TT_LOG_D(TAG, "event"); - auto* statusbar = static_cast(obj); +static void statusbar_pubsub_event(Statusbar* statusbar) { + TT_LOG_D(TAG, "Update event"); if (lock(portMAX_DELAY)) { update_main(statusbar); lv_obj_invalidate(&statusbar->obj); @@ -133,7 +132,9 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE); LV_TRACE_OBJ_CREATE("finished"); auto* statusbar = (Statusbar*)obj; - statusbar->pubsub_subscription = statusbar_data.pubsub->subscribe(&statusbar_pubsub_event, statusbar); + statusbar->pubsub_subscription = statusbar_data.pubsub->subscribe([statusbar](auto) { + statusbar_pubsub_event(statusbar); + }); if (!statusbar_data.time_update_timer->isRunning()) { statusbar_data.time_update_timer->start(200 / portTICK_PERIOD_MS); diff --git a/Tactility/Source/service/development/DevelopmentService.cpp b/Tactility/Source/service/development/DevelopmentService.cpp index a9caa94d..90f0df12 100644 --- a/Tactility/Source/service/development/DevelopmentService.cpp +++ b/Tactility/Source/service/development/DevelopmentService.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/Tactility/Source/service/espnow/EspNowService.cpp b/Tactility/Source/service/espnow/EspNowService.cpp index 9c71850d..58cc39b0 100644 --- a/Tactility/Source/service/espnow/EspNowService.cpp +++ b/Tactility/Source/service/espnow/EspNowService.cpp @@ -1,10 +1,11 @@ #ifdef ESP_PLATFORM -#include "Tactility/service/espnow/EspNowService.h" -#include "Tactility/TactilityHeadless.h" -#include "Tactility/service/ServiceManifest.h" -#include "Tactility/service/ServiceRegistration.h" -#include "Tactility/service/espnow/EspNowWifi.h" +#include +#include +#include +#include +#include + #include #include #include diff --git a/Tactility/Source/service/gps/GpsService.cpp b/Tactility/Source/service/gps/GpsService.cpp index f69ed1e6..78e272ad 100644 --- a/Tactility/Source/service/gps/GpsService.cpp +++ b/Tactility/Source/service/gps/GpsService.cpp @@ -208,7 +208,7 @@ void GpsService::setState(State newState) { lock.lock(); state = newState; lock.unlock(); - statePubSub->publish(&state); + statePubSub->publish(state); } bool GpsService::hasCoordinates() const { diff --git a/Tactility/Source/service/gui/GuiService.cpp b/Tactility/Source/service/gui/GuiService.cpp index cb03babc..3408355d 100644 --- a/Tactility/Source/service/gui/GuiService.cpp +++ b/Tactility/Source/service/gui/GuiService.cpp @@ -12,22 +12,16 @@ namespace tt::service::gui { extern const ServiceManifest manifest; -constexpr const char* TAG = "gui"; +constexpr auto* TAG = "GuiService"; // region AppManifest -void GuiService::onLoaderMessage(const void* message, TT_UNUSED void* context) { - auto service = findService(); - if (service == nullptr) { - return; - } - - auto* event = static_cast(message); - if (event->type == loader::LoaderEventTypeApplicationShowing) { +void GuiService::onLoaderEvent(loader::LoaderEvent event) { + if (event == loader::LoaderEvent::ApplicationShowing) { auto app_instance = app::getCurrentAppContext(); - service->showApp(app_instance); - } else if (event->type == loader::LoaderEventTypeApplicationHiding) { - service->hideApp(); + showApp(app_instance); + } else if (event == loader::LoaderEvent::ApplicationHiding) { + hideApp(); } } @@ -124,7 +118,12 @@ void GuiService::onStart(TT_UNUSED ServiceContext& service) { 4096, // Last known minimum was 2800 for launching desktop []() { return guiMain(); } ); - loader_pubsub_subscription = loader::getPubsub()->subscribe(&onLoaderMessage, nullptr); + + loader_pubsub_subscription = loader::getPubsub()->subscribe([this](auto event) { + onLoaderEvent(event); + }); + + tt_check(lvgl::lock(1000 / portTICK_PERIOD_MS)); keyboardGroup = lv_group_create(); auto* screen_root = lv_screen_active(); diff --git a/Tactility/Source/service/loader/Loader.cpp b/Tactility/Source/service/loader/Loader.cpp index 87f62127..a8b08ed1 100644 --- a/Tactility/Source/service/loader/Loader.cpp +++ b/Tactility/Source/service/loader/Loader.cpp @@ -3,7 +3,6 @@ #include "Tactility/app/AppManifest.h" #include "Tactility/app/AppRegistration.h" -#include #include #include #include @@ -11,7 +10,6 @@ #include #ifdef ESP_PLATFORM -#include #include #include #else @@ -20,8 +18,8 @@ namespace tt::service::loader { -#define TAG "loader" -#define LOADER_TIMEOUT (100 / portTICK_PERIOD_MS) +constexpr auto* TAG = "Loader"; +constexpr auto LOADER_TIMEOUT = (100 / portTICK_PERIOD_MS); extern const ServiceManifest manifest; @@ -47,7 +45,7 @@ static const char* appStateToString(app::State state) { class LoaderService final : public Service { - std::shared_ptr pubsubExternal = std::make_shared(); + std::shared_ptr> pubsubExternal = std::make_shared>(); Mutex mutex = Mutex(Mutex::Type::Recursive); std::stack> appStack; app::LaunchId nextLaunchId = 0; @@ -64,13 +62,13 @@ class LoaderService final : public Service { public: - void onStart(TT_UNUSED ServiceContext& service) final { + void onStart(TT_UNUSED ServiceContext& service) override { dispatcherThread->start(); } - void onStop(TT_UNUSED ServiceContext& service) final { + void onStop(TT_UNUSED ServiceContext& service) override { // Send stop signal to thread and wait for thread to finish - mutex.withLock([this]() { + mutex.withLock([this] { dispatcherThread->stop(); }); } @@ -79,7 +77,7 @@ public: void stopApp(); std::shared_ptr _Nullable getCurrentAppContext(); - std::shared_ptr getPubsub() const { return pubsubExternal; } + std::shared_ptr> getPubsub() const { return pubsubExternal; } }; std::shared_ptr _Nullable optScreenshotService() { @@ -117,8 +115,7 @@ void LoaderService::onStartAppMessage(const std::string& id, app::LaunchId launc transitionAppToState(new_app, app::State::Showing); - LoaderEvent event_external = { .type = LoaderEventTypeApplicationStarted }; - pubsubExternal->publish(&event_external); + pubsubExternal->publish(LoaderEvent::ApplicationStarted); } void LoaderService::onStopAppMessage(const std::string& id) { @@ -188,8 +185,7 @@ void LoaderService::onStopAppMessage(const std::string& id) { lock.unlock(); // WARNING: After this point we cannot change the app states from this method directly anymore as we don't have a lock! - LoaderEvent event_external = { .type = LoaderEventTypeApplicationStopped }; - pubsubExternal->publish(&event_external); + pubsubExternal->publish(LoaderEvent::ApplicationStopped); if (instance_to_resume != nullptr) { if (result_set) { @@ -240,13 +236,11 @@ void LoaderService::transitionAppToState(const std::shared_ptr app->getApp()->onCreate(*app); break; case Showing: { - LoaderEvent event_showing = { .type = LoaderEventTypeApplicationShowing }; - pubsubExternal->publish(&event_showing); + pubsubExternal->publish(LoaderEvent::ApplicationShowing); break; } case Hiding: { - LoaderEvent event_hiding = { .type = LoaderEventTypeApplicationHiding }; - pubsubExternal->publish(&event_hiding); + pubsubExternal->publish(LoaderEvent::ApplicationHiding); break; } case Stopped: @@ -307,7 +301,7 @@ std::shared_ptr _Nullable getCurrentApp() { return app_context != nullptr ? app_context->getApp() : nullptr; } -std::shared_ptr getPubsub() { +std::shared_ptr> getPubsub() { auto service = optScreenshotService(); assert(service); return service->getPubsub(); diff --git a/Tactility/Source/service/sdcard/Sdcard.cpp b/Tactility/Source/service/sdcard/Sdcard.cpp index 57502e0f..a58cc83c 100644 --- a/Tactility/Source/service/sdcard/Sdcard.cpp +++ b/Tactility/Source/service/sdcard/Sdcard.cpp @@ -1,14 +1,14 @@ -#include "Tactility/service/ServiceContext.h" -#include "Tactility/TactilityHeadless.h" -#include "Tactility/service/ServiceRegistration.h" +#include +#include #include #include - -#define TAG "sdcard_service" +#include namespace tt::service::sdcard { +constexpr auto* TAG = "SdcardService"; + extern const ServiceManifest manifest; class SdCardService final : public Service { diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index 4d9e8145..caf5d6d6 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -6,7 +6,6 @@ #include "Tactility/service/gps/GpsService.h" #include #include -#include #include #include #include @@ -14,7 +13,7 @@ namespace tt::service::statusbar { -#define TAG "statusbar_service" +constexpr auto* TAG = "StatusbarService"; // SD card status #define STATUSBAR_ICON_SDCARD "sdcard.png" diff --git a/Tactility/Source/service/wifi/WifiEsp.cpp b/Tactility/Source/service/wifi/WifiEsp.cpp index 6ec729ab..79f0b6ea 100644 --- a/Tactility/Source/service/wifi/WifiEsp.cpp +++ b/Tactility/Source/service/wifi/WifiEsp.cpp @@ -1,14 +1,14 @@ #ifdef ESP_PLATFORM -#include "Tactility/service/wifi/Wifi.h" - -#include "Tactility/TactilityHeadless.h" -#include "Tactility/service/ServiceContext.h" -#include "Tactility/service/wifi/WifiGlobals.h" -#include "Tactility/service/wifi/WifiSettings.h" -#include "Tactility/service/wifi/WifiBootSplashInit.h" +#include +#include +#include #include +#include +#include +#include +#include #include #include @@ -19,10 +19,10 @@ namespace tt::service::wifi { -#define TAG "wifi_service" -#define WIFI_CONNECTED_BIT BIT0 -#define WIFI_FAIL_BIT BIT1 -#define AUTO_SCAN_INTERVAL 10000 // ms +constexpr auto* TAG = "WifiService"; +constexpr auto WIFI_CONNECTED_BIT = BIT0; +constexpr auto WIFI_FAIL_BIT = BIT1; +constexpr auto AUTO_SCAN_INTERVAL = 10000; // ms // Forward declarations class Wifi; @@ -48,7 +48,7 @@ public: Mutex dataMutex = Mutex(Mutex::Type::Recursive); std::unique_ptr autoConnectTimer; /** @brief The public event bus */ - std::shared_ptr pubsub = std::make_shared(); + std::shared_ptr> pubsub = std::make_shared>(); // TODO: Deal with messages that come in while an action is ongoing // for example: when scanning and you turn off the radio, the scan should probably stop or turning off // the radio should disable the on/off button in the app as it is pending. @@ -129,7 +129,7 @@ static std::shared_ptr wifi_singleton; // region Public functions -std::shared_ptr getPubsub() { +std::shared_ptr> getPubsub() { auto wifi = wifi_singleton; if (wifi == nullptr) { tt_crash("Service not running"); @@ -371,11 +371,10 @@ static void scan_list_free_safely(std::shared_ptr wifi) { } } -static void publish_event_simple(std::shared_ptr wifi, EventType type) { +static void publish_event(std::shared_ptr wifi, WifiEvent event) { auto lock = wifi->dataMutex.asScopedLock(); if (lock.lock()) { - Event turning_on_event = {.type = type}; - wifi->pubsub->publish(&turning_on_event); + wifi->pubsub->publish(event); } } @@ -484,7 +483,7 @@ static void eventHandler(TT_UNUSED void* arg, esp_event_base_t event_base, int32 break; } wifi->setRadioState(RadioState::On); - publish_event_simple(wifi, EventType::Disconnected); + publish_event(wifi, WifiEvent::Disconnected); kernel::publishSystemEvent(kernel::SystemEvent::NetworkDisconnected); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { auto* event = static_cast(event_data); @@ -511,7 +510,7 @@ static void eventHandler(TT_UNUSED void* arg, esp_event_base_t event_base, int32 esp_wifi_scan_stop(); } - publish_event_simple(wifi_singleton, EventType::ScanFinished); + publish_event(wifi_singleton, WifiEvent::ScanFinished); TT_LOG_I(TAG, "eventHandler: Finished scan"); if (copied_list && wifi_singleton->getRadioState() == RadioState::On && !wifi->pause_auto_connect) { @@ -537,7 +536,7 @@ static void dispatchEnable(std::shared_ptr wifi) { if (lock.lock(50 / portTICK_PERIOD_MS)) { TT_LOG_I(TAG, "Enabling"); wifi->setRadioState(RadioState::OnPending); - publish_event_simple(wifi, EventType::RadioStateOnPending); + publish_event(wifi, WifiEvent::RadioStateOnPending); if (wifi->netif != nullptr) { esp_netif_destroy(wifi->netif); @@ -554,7 +553,7 @@ static void dispatchEnable(std::shared_ptr wifi) { TT_LOG_E(TAG, "Insufficient memory"); } wifi->setRadioState(RadioState::Off); - publish_event_simple(wifi, EventType::RadioStateOff); + publish_event(wifi, WifiEvent::RadioStateOff); return; } @@ -582,7 +581,7 @@ static void dispatchEnable(std::shared_ptr wifi) { TT_LOG_E(TAG, "Wifi mode setting failed"); wifi->setRadioState(RadioState::Off); esp_wifi_deinit(); - publish_event_simple(wifi, EventType::RadioStateOff); + publish_event(wifi, WifiEvent::RadioStateOff); return; } @@ -595,12 +594,12 @@ static void dispatchEnable(std::shared_ptr wifi) { wifi->setRadioState(RadioState::Off); esp_wifi_set_mode(WIFI_MODE_NULL); esp_wifi_deinit(); - publish_event_simple(wifi, EventType::RadioStateOff); + publish_event(wifi, WifiEvent::RadioStateOff); return; } wifi->setRadioState(RadioState::On); - publish_event_simple(wifi, EventType::RadioStateOn); + publish_event(wifi, WifiEvent::RadioStateOn); wifi->pause_auto_connect = false; @@ -631,7 +630,7 @@ static void dispatchDisable(std::shared_ptr wifi) { TT_LOG_I(TAG, "Disabling"); wifi->setRadioState(RadioState::OffPending); - publish_event_simple(wifi, EventType::RadioStateOffPending); + publish_event(wifi, WifiEvent::RadioStateOffPending); // Free up scan list memory scan_list_free_safely(wifi_singleton); @@ -639,7 +638,7 @@ static void dispatchDisable(std::shared_ptr wifi) { if (esp_wifi_stop() != ESP_OK) { TT_LOG_E(TAG, "Failed to stop radio"); wifi->setRadioState(RadioState::On); - publish_event_simple(wifi, EventType::RadioStateOn); + publish_event(wifi, WifiEvent::RadioStateOn); return; } @@ -672,7 +671,7 @@ static void dispatchDisable(std::shared_ptr wifi) { wifi->netif = nullptr; wifi->setScanActive(false); wifi->setRadioState(RadioState::Off); - publish_event_simple(wifi, EventType::RadioStateOff); + publish_event(wifi, WifiEvent::RadioStateOff); TT_LOG_I(TAG, "Disabled"); } @@ -706,7 +705,7 @@ static void dispatchScan(std::shared_ptr wifi) { TT_LOG_I(TAG, "Starting scan"); wifi->setScanActive(true); - publish_event_simple(wifi, EventType::ScanStarted); + publish_event(wifi, WifiEvent::ScanStarted); } static void dispatchConnect(std::shared_ptr wifi) { @@ -740,7 +739,7 @@ static void dispatchConnect(std::shared_ptr wifi) { wifi->setRadioState(RadioState::ConnectionPending); - publish_event_simple(wifi, EventType::ConnectionPending); + publish_event(wifi, WifiEvent::ConnectionPending); wifi_config_t config; memset(&config, 0, sizeof(wifi_config_t)); @@ -762,7 +761,7 @@ static void dispatchConnect(std::shared_ptr wifi) { if (set_config_result != ESP_OK) { wifi->setRadioState(RadioState::On); TT_LOG_E(TAG, "Failed to set wifi config (%s)", esp_err_to_name(set_config_result)); - publish_event_simple(wifi, EventType::ConnectionFailed); + publish_event(wifi, WifiEvent::ConnectionFailed); return; } @@ -771,7 +770,7 @@ static void dispatchConnect(std::shared_ptr wifi) { if (wifi_start_result != ESP_OK) { wifi->setRadioState(RadioState::On); TT_LOG_E(TAG, "Failed to start wifi to begin connecting (%s)", esp_err_to_name(wifi_start_result)); - publish_event_simple(wifi, EventType::ConnectionFailed); + publish_event(wifi, WifiEvent::ConnectionFailed); return; } @@ -784,7 +783,7 @@ static void dispatchConnect(std::shared_ptr wifi) { if (bits & WIFI_CONNECTED_BIT) { wifi->setSecureConnection(config.sta.password[0] != 0x00U); wifi->setRadioState(RadioState::ConnectionActive); - publish_event_simple(wifi, EventType::ConnectionSuccess); + publish_event(wifi, WifiEvent::ConnectionSuccess); TT_LOG_I(TAG, "Connected to %s", wifi->connection_target.ssid.c_str()); if (wifi->connection_target_remember) { if (!settings::save(wifi->connection_target)) { @@ -795,11 +794,11 @@ static void dispatchConnect(std::shared_ptr wifi) { } } else if (bits & WIFI_FAIL_BIT) { wifi->setRadioState(RadioState::On); - publish_event_simple(wifi, EventType::ConnectionFailed); + 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_simple(wifi, EventType::ConnectionFailed); + publish_event(wifi, WifiEvent::ConnectionFailed); TT_LOG_E(TAG, "UNEXPECTED EVENT"); } @@ -834,7 +833,7 @@ static void dispatchDisconnectButKeepActive(std::shared_ptr wifi) { // TODO: disable radio, because radio state is in limbo between off and on wifi->setRadioState(RadioState::Off); TT_LOG_E(TAG, "failed to set wifi config (%s)", esp_err_to_name(set_config_result)); - publish_event_simple(wifi, EventType::RadioStateOff); + publish_event(wifi, WifiEvent::RadioStateOff); return; } @@ -843,12 +842,12 @@ static void dispatchDisconnectButKeepActive(std::shared_ptr wifi) { // TODO: disable radio, because radio state is in limbo between off and on wifi->setRadioState(RadioState::Off); TT_LOG_E(TAG, "failed to start wifi to begin connecting (%s)", esp_err_to_name(wifi_start_result)); - publish_event_simple(wifi, EventType::RadioStateOff); + publish_event(wifi, WifiEvent::RadioStateOff); return; } wifi->setRadioState(RadioState::On); - publish_event_simple(wifi, EventType::Disconnected); + publish_event(wifi, WifiEvent::Disconnected); TT_LOG_I(TAG, "Disconnected"); } diff --git a/Tactility/Source/service/wifi/WifiMock.cpp b/Tactility/Source/service/wifi/WifiMock.cpp index 3d3f7a41..dfcd0eb5 100644 --- a/Tactility/Source/service/wifi/WifiMock.cpp +++ b/Tactility/Source/service/wifi/WifiMock.cpp @@ -22,7 +22,7 @@ struct Wifi { /** @brief Locking mechanism for modifying the Wifi instance */ Mutex mutex = Mutex(Mutex::Type::Recursive); /** @brief The public event bus */ - std::shared_ptr pubsub = std::make_shared(); + std::shared_ptr> pubsub = std::make_shared>(); /** @brief The internal message queue */ bool scan_active = false; bool secure_connection = false; @@ -34,16 +34,15 @@ static Wifi* wifi = nullptr; // region Static -static void publish_event_simple(Wifi* wifi, EventType type) { - Event turning_on_event = { .type = type }; - wifi->pubsub->publish(&turning_on_event); +static void publish_event(WifiEvent event) { + wifi->pubsub->publish(event); } // endregion Static // region Public functions -std::shared_ptr getPubsub() { +std::shared_ptr> getPubsub() { assert(wifi); return wifi->pubsub; } diff --git a/Tactility/Source/service/wifi/WifiSettings.cpp b/Tactility/Source/service/wifi/WifiSettings.cpp index d46a6905..da4ebd6f 100644 --- a/Tactility/Source/service/wifi/WifiSettings.cpp +++ b/Tactility/Source/service/wifi/WifiSettings.cpp @@ -1,9 +1,8 @@ -#include "Tactility/service/wifi/WifiSettings.h" -#include "Tactility/Preferences.h" -#include "Tactility/file/PropertiesFile.h" +#include -#include #include +#include +#include namespace tt::service::wifi::settings { @@ -15,6 +14,12 @@ struct WifiSettings { bool enableOnBoot; }; +static WifiSettings cachedSettings { + .enableOnBoot = false +}; + +static bool cached = false; + static bool load(WifiSettings& settings) { std::map map; if (!file::loadPropertiesFile(SETTINGS_FILE, map)) { @@ -36,19 +41,27 @@ static bool save(const WifiSettings& settings) { return file::savePropertiesFile(SETTINGS_FILE, map); } +WifiSettings getCachedOrLoad() { + if (!cached) { + if (!load(cachedSettings)) { + TT_LOG_E(TAG, "Failed to load %s", SETTINGS_FILE); + } else { + cached = true; + } + } + + return cachedSettings; +} + void setEnableOnBoot(bool enable) { - WifiSettings settings { .enableOnBoot = enable }; - if (!save(settings)) { + cachedSettings.enableOnBoot = enable; + if (!save(cachedSettings)) { TT_LOG_E(TAG, "Failed to save %s", SETTINGS_FILE); } } bool shouldEnableOnBoot() { - WifiSettings settings; - if (!load(settings)) { - return false; - } - return settings.enableOnBoot; + return getCachedOrLoad().enableOnBoot; } } // namespace diff --git a/Tactility/Source/settings/SystemSettings.cpp b/Tactility/Source/settings/SystemSettings.cpp index 67d0d347..05d960a4 100644 --- a/Tactility/Source/settings/SystemSettings.cpp +++ b/Tactility/Source/settings/SystemSettings.cpp @@ -7,11 +7,11 @@ namespace tt::settings { constexpr auto* TAG = "SystemSettings"; -constexpr auto* FILE_PATH = "/data/system.properties"; +constexpr auto* FILE_PATH = "/data/settings/system.properties"; static Mutex mutex = Mutex(); static bool cached = false; -static SystemSettings cachedProperties; +static SystemSettings cachedSettings; static bool loadSystemSettingsFromFile(SystemSettings& properties) { std::map map; @@ -44,13 +44,13 @@ bool loadSystemSettings(SystemSettings& properties) { scoped_lock.lock(); if (!cached) { - if (!loadSystemSettingsFromFile(cachedProperties)) { + if (!loadSystemSettingsFromFile(cachedSettings)) { return false; } cached = true; } - properties = cachedProperties; + properties = cachedSettings; return true; } @@ -68,7 +68,7 @@ bool saveSystemSettings(const SystemSettings& properties) { return false; } - cachedProperties = properties; + cachedSettings = properties; cached = true; return true; }); diff --git a/TactilityC/Include/tt_app.h b/TactilityC/Include/tt_app.h index 884bd5df..03bfdd3b 100644 --- a/TactilityC/Include/tt_app.h +++ b/TactilityC/Include/tt_app.h @@ -7,6 +7,7 @@ extern "C" { #endif typedef void* AppHandle; +typedef void* AppPathsHandle; /** @return the bundle that belongs to this application, or null if it wasn't started with parameters. */ BundleHandle _Nullable tt_app_get_parameters(AppHandle handle); @@ -14,7 +15,7 @@ BundleHandle _Nullable tt_app_get_parameters(AppHandle handle); /** * Set the result before closing an app. * The result and bundle are passed along to the app that launched this app, when this app is closed. - * @param[in] handle the app context handle to set the result for + * @param[in] handle the app handle to set the result for * @param[in] result the result state to set * @param[in] bundle the result bundle to set */ @@ -23,6 +24,21 @@ void tt_app_set_result(AppHandle handle, AppResult result, BundleHandle _Nullabl /** @return true if a result was set for this app context */ bool tt_app_has_result(AppHandle handle); +/** Get the path to the data directory of this app. + * @param[in] handle the app handle + * @param[out] buffer the output buffer (recommended size is 256 bytes) + * @param[inout] size used as input for maximum buffer size (including null terminator) and is set with the path string length by this function + */ +void tt_app_get_data_directory(AppPathsHandle handle, char* buffer, size_t& size); + +/** Get the path to the data directory of this app, with LVGL drive letter prefix applied. + * The recommended buffer size is 256 bytes. + * @param[in] handle the app handle + * @param[out] buffer the output buffer (recommended size is 256 bytes) + * @param[inout] size used as input for maximum buffer size (including null terminator) and is set with the path string length by this function + */ +void tt_app_get_data_directory_lvgl(AppPathsHandle handle, char* buffer, size_t& size); + /** * Start an app by id. * @param[in] appId the app manifest id diff --git a/TactilityC/Source/tt_app.cpp b/TactilityC/Source/tt_app.cpp index 2a50ddbf..ad152b36 100644 --- a/TactilityC/Source/tt_app.cpp +++ b/TactilityC/Source/tt_app.cpp @@ -4,6 +4,8 @@ extern "C" { +constexpr auto* TAG = "tt_app"; + #define HANDLE_AS_APP_CONTEXT(handle) ((tt::app::AppContext*)(handle)) BundleHandle _Nullable tt_app_get_parameters(AppHandle handle) { @@ -31,4 +33,38 @@ void tt_app_stop() { tt::app::stop(); } +void tt_app_get_data_directory(AppPathsHandle handle, char* buffer, size_t& size) { + assert(buffer != nullptr); + assert(size > 0); + auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); + auto data_path = paths->getDataDirectory(); + auto expected_length = data_path.length() + 1; + if (size < expected_length) { + TT_LOG_E(TAG, "Path buffer not large enough (%d < %d)", size, expected_length); + size = 0; + buffer[0] = 0; + return; + } + + strcpy(buffer, data_path.c_str()); + size = data_path.length(); +} + +void tt_app_get_data_directory_lvgl(AppPathsHandle handle, char* buffer, size_t& size) { + assert(buffer != nullptr); + assert(size > 0); + auto paths = HANDLE_AS_APP_CONTEXT(handle)->getPaths(); + auto data_path = paths->getDataDirectoryLvgl(); + auto expected_length = data_path.length() + 1; + if (size < expected_length) { + TT_LOG_E(TAG, "Path buffer not large enough (%d < %d)", size, expected_length); + size = 0; + buffer[0] = 0; + return; + } + + strcpy(buffer, data_path.c_str()); + size = data_path.length(); +} + } \ No newline at end of file diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 8a2142d5..d741108c 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -119,6 +119,8 @@ const esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(tt_app_selectiondialog_get_result_index), ESP_ELFSYM_EXPORT(tt_app_alertdialog_start), ESP_ELFSYM_EXPORT(tt_app_alertdialog_get_result_index), + ESP_ELFSYM_EXPORT(tt_app_get_data_directory), + ESP_ELFSYM_EXPORT(tt_app_get_data_directory_lvgl), ESP_ELFSYM_EXPORT(tt_bundle_alloc), ESP_ELFSYM_EXPORT(tt_bundle_free), ESP_ELFSYM_EXPORT(tt_bundle_opt_bool), diff --git a/TactilityCore/Include/Tactility/Bundle.h b/TactilityCore/Include/Tactility/Bundle.h index 559e658b..9b5c70bf 100644 --- a/TactilityCore/Include/Tactility/Bundle.h +++ b/TactilityCore/Include/Tactility/Bundle.h @@ -13,9 +13,7 @@ namespace tt { /** * A dictionary that maps keys (strings) onto several atomary types. */ -class Bundle { - -private: +class Bundle final { typedef uint32_t Hash; diff --git a/TactilityCore/Include/Tactility/Dispatcher.h b/TactilityCore/Include/Tactility/Dispatcher.h index f8462aed..114eb639 100644 --- a/TactilityCore/Include/Tactility/Dispatcher.h +++ b/TactilityCore/Include/Tactility/Dispatcher.h @@ -19,7 +19,8 @@ namespace tt { * Generally, one task would dispatch the execution, * while the other thread consumes and executes the work. */ -class Dispatcher { +class Dispatcher final { + public: typedef std::function Function; @@ -38,8 +39,10 @@ public: /** * 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) */ - void dispatch(Function function, TickType_t timeout = portMAX_DELAY); + bool dispatch(Function function, TickType_t timeout = portMAX_DELAY); /** * Consume 1 or more dispatched function (if any) until the queue is empty. diff --git a/TactilityCore/Include/Tactility/DispatcherThread.h b/TactilityCore/Include/Tactility/DispatcherThread.h index 7683aab7..2e0f1557 100644 --- a/TactilityCore/Include/Tactility/DispatcherThread.h +++ b/TactilityCore/Include/Tactility/DispatcherThread.h @@ -5,11 +5,11 @@ namespace tt { /** Starts a Thread to process dispatched messages */ -class DispatcherThread { +class DispatcherThread final { Dispatcher dispatcher; std::unique_ptr thread; - bool interruptThread = false; + bool interruptThread = true; int32_t threadMain(); @@ -21,13 +21,16 @@ public: /** * Dispatch a message. */ - void dispatch(Dispatcher::Function function, TickType_t timeout = portMAX_DELAY); + 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; } }; } \ No newline at end of file diff --git a/TactilityCore/Include/Tactility/EventFlag.h b/TactilityCore/Include/Tactility/EventFlag.h index d7949762..91319f43 100644 --- a/TactilityCore/Include/Tactility/EventFlag.h +++ b/TactilityCore/Include/Tactility/EventFlag.h @@ -8,8 +8,7 @@ namespace tt { /** * Wrapper for FreeRTOS xEventGroup. */ -class EventFlag { -private: +class EventFlag final { struct EventGroupHandleDeleter { void operator()(EventGroupHandle_t handleToDelete) { diff --git a/TactilityCore/Include/Tactility/PubSub.h b/TactilityCore/Include/Tactility/PubSub.h index 5c5c332d..77dd087f 100644 --- a/TactilityCore/Include/Tactility/PubSub.h +++ b/TactilityCore/Include/Tactility/PubSub.h @@ -1,7 +1,3 @@ -/** - * @file pubsub.h - * PubSub - */ #pragma once #include "Mutex.h" @@ -9,18 +5,13 @@ namespace tt { -/** PubSub Callback type */ -typedef void (*PubSubCallback)(const void* message, void* context); - /** Publish and subscribe to messages in a thread-safe manner. */ -class PubSub { - -private: +template +class PubSub final { struct Subscription { uint64_t id; - PubSubCallback callback; - void* callbackParameter; + std::function callback; }; typedef std::list Subscriptions; @@ -42,21 +33,55 @@ public: /** Start receiving messages at the specified handle (Threadsafe, Re-entrable) * @param[in] callback - * @param[in] callbackParameter the data to pass to the callback * @return subscription instance */ - SubscriptionHandle subscribe(PubSubCallback callback, void* callbackParameter); + SubscriptionHandle subscribe(std::function callback) { + mutex.lock(); + items.push_back({ + .id = (++lastId), + .callback = std::move(callback) + }); + + mutex.unlock(); + + return reinterpret_cast(lastId); + } /** Stop receiving messages at the specified handle (Threadsafe, Re-entrable.) * No use of `tt_pubsub_subscription` allowed after call of this method * @param[in] subscription */ - void unsubscribe(SubscriptionHandle subscription); + void unsubscribe(SubscriptionHandle subscription) { + assert(subscription); - /** Publish message to all subscribers (Threadsafe, Re-entrable.) - * @param[in] message message pointer to publish - it is passed as-is to the callback + mutex.lock(); + bool result = false; + auto id = reinterpret_cast(subscription); + for (auto it = items.begin(); it != items.end(); ++it) { + if (it->id == id) { + items.erase(it); + result = true; + break; + } + } + + mutex.unlock(); + tt_check(result); + } + + /** Publish something to all subscribers (Threadsafe, Re-entrable.) + * @param[in] data the data to publish */ - void publish(void* message); + void publish(DataType data) { + mutex.lock(); + + // Iterate over subscribers + for (auto& it : items) { + it.callback(data); + } + + mutex.unlock(); + } }; diff --git a/TactilityCore/Include/Tactility/Semaphore.h b/TactilityCore/Include/Tactility/Semaphore.h index 788e2fea..756363ca 100644 --- a/TactilityCore/Include/Tactility/Semaphore.h +++ b/TactilityCore/Include/Tactility/Semaphore.h @@ -21,8 +21,6 @@ namespace tt { */ class Semaphore final : public Lock { -private: - struct SemaphoreHandleDeleter { void operator()(QueueHandle_t handleToDelete) { assert(!kernel::isIsr()); diff --git a/TactilityCore/Include/Tactility/StreamBuffer.h b/TactilityCore/Include/Tactility/StreamBuffer.h index 94c6fb82..b85e5b8a 100644 --- a/TactilityCore/Include/Tactility/StreamBuffer.h +++ b/TactilityCore/Include/Tactility/StreamBuffer.h @@ -22,9 +22,7 @@ namespace tt { * 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 { - -private: +class StreamBuffer final { struct StreamBufferHandleDeleter { void operator()(StreamBufferHandle_t handleToDelete) { diff --git a/TactilityCore/Include/Tactility/Thread.h b/TactilityCore/Include/Tactility/Thread.h index 98de178b..6d623e6e 100644 --- a/TactilityCore/Include/Tactility/Thread.h +++ b/TactilityCore/Include/Tactility/Thread.h @@ -1,6 +1,5 @@ #pragma once -#include "CoreDefines.h" #include "RtosCompatTask.h" #include @@ -11,7 +10,7 @@ namespace tt { typedef TaskHandle_t ThreadId; -class Thread { +class Thread final { public: @@ -184,8 +183,8 @@ public: static uint32_t awaitFlags(uint32_t flags, uint32_t options, uint32_t timeout); }; -#define THREAD_PRIORITY_SERVICE Thread::Priority::High -#define THREAD_PRIORITY_RENDER Thread::Priority::Higher -#define THREAD_PRIORITY_ISR Thread::Priority::Critical +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 diff --git a/TactilityCore/Source/Dispatcher.cpp b/TactilityCore/Source/Dispatcher.cpp index 756e7d1f..0836602e 100644 --- a/TactilityCore/Source/Dispatcher.cpp +++ b/TactilityCore/Source/Dispatcher.cpp @@ -15,7 +15,7 @@ Dispatcher::~Dispatcher() { mutex.unlock(); } -void Dispatcher::dispatch(Function function, TickType_t timeout) { +bool Dispatcher::dispatch(Function function, TickType_t timeout) { // Mutate if (mutex.lock(timeout)) { queue.push(std::move(function)); @@ -25,8 +25,10 @@ void Dispatcher::dispatch(Function function, TickType_t timeout) { tt_check(mutex.unlock()); // Signal eventFlag.set(WAIT_FLAG); + return true; } else { TT_LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED); + return false; } } diff --git a/TactilityCore/Source/DispatcherThread.cpp b/TactilityCore/Source/DispatcherThread.cpp index f065bdf3..320023cb 100644 --- a/TactilityCore/Source/DispatcherThread.cpp +++ b/TactilityCore/Source/DispatcherThread.cpp @@ -6,7 +6,7 @@ DispatcherThread::DispatcherThread(const std::string& threadName, size_t threadS thread = std::make_unique( threadName, threadStackSize, - [this]() { + [this] { return threadMain(); } ); @@ -30,8 +30,8 @@ int32_t DispatcherThread::threadMain() { return 0; } -void DispatcherThread::dispatch(Dispatcher::Function function, TickType_t timeout) { - dispatcher.dispatch(function, timeout); +bool DispatcherThread::dispatch(Dispatcher::Function function, TickType_t timeout) { + return dispatcher.dispatch(function, timeout); } void DispatcherThread::start() { diff --git a/TactilityCore/Source/PubSub.cpp b/TactilityCore/Source/PubSub.cpp deleted file mode 100644 index d99b547d..00000000 --- a/TactilityCore/Source/PubSub.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "Tactility/PubSub.h" -#include "Tactility/Check.h" - -namespace tt { - -PubSub::SubscriptionHandle PubSub::subscribe(PubSubCallback callback, void* callbackParameter) { - mutex.lock(); - items.push_back({ - .id = (++lastId), - .callback = callback, - .callbackParameter = callbackParameter}); - - mutex.unlock(); - - return (Subscription*)lastId; -} - -void PubSub::unsubscribe(SubscriptionHandle subscription) { - assert(subscription); - - mutex.lock(); - bool result = false; - auto id = (uint64_t)subscription; - for (auto it = items.begin(); it != items.end(); it++) { - if (it->id == id) { - items.erase(it); - result = true; - break; - } - } - - mutex.unlock(); - tt_check(result); -} - -void PubSub::publish(void* message) { - mutex.lock(); - - // Iterate over subscribers - for (auto& it : items) { - it.callback(message, it.callbackParameter); - } - - mutex.unlock(); -} - -} // namespace diff --git a/TactilityCore/Source/StreamBuffer.cpp b/TactilityCore/Source/StreamBuffer.cpp index 0b9ffdec..0c1372af 100644 --- a/TactilityCore/Source/StreamBuffer.cpp +++ b/TactilityCore/Source/StreamBuffer.cpp @@ -5,7 +5,7 @@ namespace tt { -inline static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) { +static StreamBufferHandle_t createStreamBuffer(size_t size, size_t triggerLevel) { assert(size != 0); return xStreamBufferCreate(size, triggerLevel); } diff --git a/Tests/TactilityCore/DispatcherThreadTest.cpp b/Tests/TactilityCore/DispatcherThreadTest.cpp new file mode 100644 index 00000000..346d509a --- /dev/null +++ b/Tests/TactilityCore/DispatcherThreadTest.cpp @@ -0,0 +1,29 @@ +#include "doctest.h" +#include +#include + +using namespace tt; + +TEST_CASE("DispatcherThread state test") { + DispatcherThread thread("test"); + CHECK_EQ(thread.isStarted(), false); + + thread.start(); + CHECK_EQ(thread.isStarted(), true); + + thread.stop(); + CHECK_EQ(thread.isStarted(), false); +} + +TEST_CASE("DispatcherThread should consume jobs") { + DispatcherThread thread("test"); + thread.start(); + int counter = 0; + + thread.dispatch([&counter]() { counter++; }); + + tt::kernel::delayTicks(10); + + CHECK_EQ(counter, 1); + thread.stop(); +} diff --git a/Tests/TactilityCore/PubSubTest.cpp b/Tests/TactilityCore/PubSubTest.cpp new file mode 100644 index 00000000..600233be --- /dev/null +++ b/Tests/TactilityCore/PubSubTest.cpp @@ -0,0 +1,35 @@ +#include "doctest.h" +#include +#include + +using namespace tt; + +TEST_CASE("PubSub publishing with no subscriptions should not crash") { + PubSub pubsub; + pubsub.publish(1); +} + +TEST_CASE("PubSub subscription receives published data") { + PubSub pubsub; + int value = 0; + + auto subscription = pubsub.subscribe([&value](auto newValue) { + value = newValue; + }); + pubsub.publish(1); + + CHECK_EQ(value, 1); +} + +TEST_CASE("PubSub unsubscribed subscription does not receive published data") { + PubSub pubsub; + int value = 0; + + auto subscription = pubsub.subscribe([&value](auto newValue) { + value = newValue; + }); + pubsub.unsubscribe(subscription); + pubsub.publish(1); + + CHECK_EQ(value, 0); +} diff --git a/sdkconfig.board.cyd-2432s024c b/sdkconfig.board.cyd-2432s024c index 9323e8c0..985e4b66 100644 --- a/sdkconfig.board.cyd-2432s024c +++ b/sdkconfig.board.cyd-2432s024c @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.cyd-2432s028r b/sdkconfig.board.cyd-2432s028r index 07423230..f0e4672a 100644 --- a/sdkconfig.board.cyd-2432s028r +++ b/sdkconfig.board.cyd-2432s028r @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.cyd-2432s032c b/sdkconfig.board.cyd-2432s032c index 2a32068c..2be114c9 100644 --- a/sdkconfig.board.cyd-2432s032c +++ b/sdkconfig.board.cyd-2432s032c @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.cyd-4848s040c b/sdkconfig.board.cyd-4848s040c index 85d0a54a..e1fa45ef 100644 --- a/sdkconfig.board.cyd-4848s040c +++ b/sdkconfig.board.cyd-4848s040c @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.cyd-8048s043c b/sdkconfig.board.cyd-8048s043c index be9659c4..d2f47ef3 100644 --- a/sdkconfig.board.cyd-8048s043c +++ b/sdkconfig.board.cyd-8048s043c @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.cyd-jc2432w328c b/sdkconfig.board.cyd-jc2432w328c index 45ba3c90..f1b89663 100644 --- a/sdkconfig.board.cyd-jc2432w328c +++ b/sdkconfig.board.cyd-jc2432w328c @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.cyd-jc8048w550c b/sdkconfig.board.cyd-jc8048w550c index fcb52cd5..59f9e7dd 100644 --- a/sdkconfig.board.cyd-jc8048w550c +++ b/sdkconfig.board.cyd-jc8048w550c @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.elecrow-crowpanel-advance-28 b/sdkconfig.board.elecrow-crowpanel-advance-28 index a8a2eea1..0e37da3a 100644 --- a/sdkconfig.board.elecrow-crowpanel-advance-28 +++ b/sdkconfig.board.elecrow-crowpanel-advance-28 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.elecrow-crowpanel-advance-35 b/sdkconfig.board.elecrow-crowpanel-advance-35 index ee6d6403..aeaee2cc 100644 --- a/sdkconfig.board.elecrow-crowpanel-advance-35 +++ b/sdkconfig.board.elecrow-crowpanel-advance-35 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.elecrow-crowpanel-advance-50 b/sdkconfig.board.elecrow-crowpanel-advance-50 index 73c1d1a8..32bdccbe 100644 --- a/sdkconfig.board.elecrow-crowpanel-advance-50 +++ b/sdkconfig.board.elecrow-crowpanel-advance-50 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.elecrow-crowpanel-basic-28 b/sdkconfig.board.elecrow-crowpanel-basic-28 index b9e5395e..04241aa3 100644 --- a/sdkconfig.board.elecrow-crowpanel-basic-28 +++ b/sdkconfig.board.elecrow-crowpanel-basic-28 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.elecrow-crowpanel-basic-35 b/sdkconfig.board.elecrow-crowpanel-basic-35 index 3907565d..826ae273 100644 --- a/sdkconfig.board.elecrow-crowpanel-basic-35 +++ b/sdkconfig.board.elecrow-crowpanel-basic-35 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.elecrow-crowpanel-basic-50 b/sdkconfig.board.elecrow-crowpanel-basic-50 index 8ea89fcd..163a385d 100644 --- a/sdkconfig.board.elecrow-crowpanel-basic-50 +++ b/sdkconfig.board.elecrow-crowpanel-basic-50 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.lilygo-tdeck b/sdkconfig.board.lilygo-tdeck index aa73175e..edb5a53e 100644 --- a/sdkconfig.board.lilygo-tdeck +++ b/sdkconfig.board.lilygo-tdeck @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.lilygo-tlora-pager b/sdkconfig.board.lilygo-tlora-pager index d31a9405..1aa20587 100644 --- a/sdkconfig.board.lilygo-tlora-pager +++ b/sdkconfig.board.lilygo-tlora-pager @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.m5stack-core2 b/sdkconfig.board.m5stack-core2 index 3ad7fb7f..2c472ac7 100644 --- a/sdkconfig.board.m5stack-core2 +++ b/sdkconfig.board.m5stack-core2 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.m5stack-cores3 b/sdkconfig.board.m5stack-cores3 index 27b2e87e..1f25a280 100644 --- a/sdkconfig.board.m5stack-cores3 +++ b/sdkconfig.board.m5stack-cores3 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.unphone b/sdkconfig.board.unphone index 82359473..76903404 100644 --- a/sdkconfig.board.unphone +++ b/sdkconfig.board.unphone @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/sdkconfig.board.waveshare-s3-touch-43 b/sdkconfig.board.waveshare-s3-touch-43 index c89e2b2c..6ae49bfb 100644 --- a/sdkconfig.board.waveshare-s3-touch-43 +++ b/sdkconfig.board.waveshare-s3-touch-43 @@ -19,7 +19,7 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=5120 CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"