Remove and refactor old HAL components

This commit is contained in:
Ken Van Hoeylandt 2026-07-24 01:34:08 +02:00
parent 29e80cfd65
commit 0d10797e92
59 changed files with 201 additions and 2442 deletions

View File

@ -5,7 +5,6 @@
#include <Tactility/SystemEvents.h>
#include <Tactility/LogMessages.h>
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/gps/GpsConfiguration.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/lvgl/LvglSync.h>

View File

@ -4,7 +4,6 @@
#include <Tactility/LogMessages.h>
#include <Tactility/SystemEvents.h>
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/gps/GpsConfiguration.h>
#include <Tactility/kernel/Kernel.h>
#include <Tactility/service/gps/GpsService.h>

View File

@ -2,7 +2,6 @@
#include "hal/SdlKeyboard.h"
#include "hal/SimulatorPower.h"
#include <Tactility/hal/Configuration.h>
#define TAG "hardware"

View File

@ -1,6 +1,5 @@
#include <Tactility/Tactility.h>
#include <tactility/driver.h>
#include <devicetree.h>
#ifdef ESP_PLATFORM
@ -9,30 +8,15 @@
#include <Simulator.h>
#endif
#ifdef CONFIG_TT_USE_DEPRECATED_HAL
// Each board project declares this variable
extern const tt::hal::Configuration hardwareConfiguration;
#else
// Legacy placeholder (required until legacy HAL is cleaned up everywhere)
extern const tt::hal::Configuration hardwareConfiguration = {};
#endif
extern "C" {
void app_main() {
static const tt::Configuration config = {
/**
* Auto-select a board based on the ./sdkconfig.board.* file
* that you copied to ./sdkconfig before you opened this project.
*/
.hardware = &hardwareConfiguration
};
#ifdef ESP_PLATFORM
tt_init_tactility_c(); // ELF bindings for side-loading on ESP32
#endif
tt::run(config, dts_modules, dts_devices);
tt::run(dts_modules, dts_devices);
}
} // extern

View File

@ -1,11 +1,9 @@
#pragma once
#include "tactility/concurrent/dispatcher.h"
#include "tactility/device.h"
#include "tactility/module.h"
#include <tactility/concurrent/dispatcher.h>
#include <tactility/device.h>
#include <tactility/module.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/hal/Configuration.h>
#include <Tactility/service/ServiceManifest.h>
#include <functional>
@ -40,27 +38,12 @@ private:
DispatcherHandle_t handle;
};
/** @brief The configuration for the operating system
* It contains the hardware configuration, apps and services
*/
struct Configuration {
/** HAL configuration (drivers) */
const hal::Configuration* hardware = nullptr;
};
/**
* @brief Main entry point for Tactility.
* @param dtsModules List of modules from devicetree, null-terminated, non-null parameter
* @param dtsDevices Array that is terminated with DTS_DEVICE_TERMINATOR
*/
void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices[]);
/**
* While technically nullable, this instance is always set if tt_init() succeeds.
* Could return nullptr if init was not called.
* @return the Configuration instance that was passed on to tt_init() if init is successful
*/
const Configuration* getConfiguration();
void run(Module* dtsModules[], DtsDevice dtsDevices[]);
/** Provides access to the dispatcher that runs on the main task.
* @warning This dispatcher is used for WiFi and might block for some time during WiFi connection.
@ -68,11 +51,4 @@ const Configuration* getConfiguration();
*/
MainDispatcher getMainDispatcher();
namespace hal {
/** While technically this configuration is nullable, it's never null after initHeadless() is called. */
const Configuration* getConfiguration();
} // namespace hal
} // namespace tt

View File

@ -1,24 +0,0 @@
#pragma once
#include <memory>
#include <tactility/hal/Device.h>
#include <vector>
namespace tt::hal {
typedef bool (*InitBoot)();
typedef std::vector<std::shared_ptr<Device>> DeviceVector;
typedef std::shared_ptr<Device> (*CreateDevice)();
struct Configuration {
/**
* Used for powering on the peripherals manually.
*/
const InitBoot initBoot = nullptr;
const std::function<DeviceVector()> createDevices = [] { return DeviceVector(); };
};
} // namespace

View File

@ -1,63 +0,0 @@
#pragma once
#include <tactility/hal/Device.h>
#include <lvgl.h>
namespace tt::hal::touch {
class TouchDevice;
}
namespace tt::hal::display {
class DisplayDriver;
class DisplayDevice : public Device {
public:
Type getType() const override { return Type::Display; }
/** Starts the internal driver */
virtual bool start() = 0;
virtual bool stop() = 0;
virtual void setPowerOn(bool turnOn) {}
virtual bool isPoweredOn() const { return true; }
virtual bool supportsPowerControl() const { return false; }
/** For e-paper screens */
virtual void requestFullRefresh() {}
/** Blocks until any frame already handed to this display has physically
* finished drawing. Displays that draw synchronously within their flush
* callback can rely on the default no-op; displays with an asynchronous
* refresh pipeline (e.g. e-paper, where a full refresh can take seconds)
* should override this so callers can safely do something irreversible
* (like cutting power) right after a screen update. */
virtual void waitForFlushComplete() {}
/** Could return nullptr if not started */
virtual std::shared_ptr<touch::TouchDevice> getTouchDevice() = 0;
/** Set a value in the range [0, 255] */
virtual void setBacklightDuty(uint8_t backlightDuty) { /* NO-OP */ }
virtual bool supportsBacklightDuty() const { return false; }
/** Set a value in the range [0, 255] */
virtual void setGammaCurve(uint8_t index) { /* NO-OP */ }
virtual uint8_t getGammaCurveCount() const { return 0; }
virtual bool supportsLvgl() const = 0;
virtual bool startLvgl() = 0;
virtual bool stopLvgl() = 0;
/** Could return nullptr if not started */
virtual lv_display_t* getLvglDisplay() const = 0;
virtual bool supportsDisplayDriver() const = 0;
/** Could return nullptr if not supported */
virtual std::shared_ptr<DisplayDriver> getDisplayDriver() = 0;
};
} // namespace tt::hal::display

View File

@ -1,37 +0,0 @@
#pragma once
#include <Tactility/Lock.h>
#include <cstdint>
namespace tt::hal::display {
enum class ColorFormat {
Monochrome, // 1 bpp
BGR565,
BGR565Swapped,
RGB565,
RGB565Swapped,
RGB888
};
class DisplayDriver {
public:
virtual ~DisplayDriver() = default;
virtual ColorFormat getColorFormat() const = 0;
virtual uint16_t getPixelWidth() const = 0;
virtual uint16_t getPixelHeight() const = 0;
virtual bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) = 0;
/**
* Returns direct pointers to the panel's hardware frame buffer(s), if the
* underlying driver supports it (DPI/MIPI-DSI panels only).
* @param[out] outBuffers receives up to 2 frame buffer pointers
* @return number of buffers written to outBuffers (0 if unsupported)
*/
virtual uint8_t getFrameBuffers(void* outBuffers[2]) const { return 0; }
};
}

View File

@ -1,26 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <Tactility/hal/display/DisplayDriver.h>
#include <tactility/device.h>
namespace tt::hal::display {
/** Wraps a TactilityKernel Device of type DISPLAY_TYPE as a DisplayDriver. */
class KernelDisplayDriver final : public DisplayDriver {
::Device* device;
public:
explicit KernelDisplayDriver(::Device* device);
ColorFormat getColorFormat() const override;
uint16_t getPixelWidth() const override;
uint16_t getPixelHeight() const override;
bool drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) override;
uint8_t getFrameBuffers(void* outBuffers[2]) const override;
};
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <tactility/hal/Device.h>
#include <lvgl.h>
namespace tt::hal::encoder {
class Display;
class EncoderDevice : public Device {
public:
Type getType() const override { return Type::Encoder; }
virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stopLvgl() = 0;
/** Could return nullptr if not started */
virtual lv_indev_t* getLvglIndev() = 0;
};
}

View File

@ -1,34 +0,0 @@
#pragma once
#include <cstdint>
namespace tt::hal::gpio {
typedef unsigned int Pin;
constexpr Pin NO_PIN = -1;
/** @warning The order must match GpioMode from tt_hal_gpio.h */
enum class Mode {
Disable = 0,
Input,
Output,
OutputOpenDrain,
InputOutput,
InputOutputOpenDrain
};
/** Configure a single pin */
bool configure(Pin pin, Mode mode, bool pullUp, bool pullDown);
/** Configure a set of pins defined by their bit index */
bool configureWithPinBitmask(uint64_t pinBitMask, Mode mode, bool pullUp, bool pullDown);
bool setMode(Pin pin, Mode mode);
bool getLevel(Pin pin);
bool setLevel(Pin pin, bool level);
int getPinCount();
}

View File

@ -46,7 +46,10 @@ private:
std::shared_ptr<std::function<void(Device::Id id, const minmea_sentence_rmc&)>> onData;
};
const GpsConfiguration configuration;
::Device* uartDevice;
uint32_t baudRate;
GpsModel model;
RecursiveMutex mutex;
std::unique_ptr<Thread> thread;
bool threadInterrupted = false;
@ -54,7 +57,6 @@ private:
std::vector<RmcSubscription> rmcSubscriptions;
GgaSubscriptionId lastSatelliteSubscriptionId = 0;
RmcSubscriptionId lastRmcSubscriptionId = 0;
GpsModel model = GpsModel::Unknown;
State state = State::Off;
int32_t threadMain();
@ -65,9 +67,18 @@ private:
public:
explicit GpsDevice(GpsConfiguration configuration) : configuration(std::move(configuration)) {}
explicit GpsDevice(
::Device* uartDevice,
uint32_t baudRate,
GpsModel model // Choosing "Unknown" will result in a probe
) : uartDevice(uartDevice), baudRate(baudRate), model(model) {
assert(uartDevice != nullptr);
device_get(uartDevice);
};
~GpsDevice() override = default;
~GpsDevice() override {
device_put(uartDevice);
}
Type getType() const override { return Type::Gps; }

View File

@ -1,49 +0,0 @@
#pragma once
#include <tactility/hal/Device.h>
#include <tactility/drivers/i2c_controller.h>
namespace tt::hal::i2c {
/**
* Represents an I2C peripheral at a specific port and address.
* It helps to read and write registers.
*
* All read and write calls are thread-safe.
* @deprecated Use the device API from the Kernel project
*/
class I2cDevice : public Device {
protected:
::Device* controller;
uint8_t address;
static constexpr TickType_t DEFAULT_TIMEOUT = 1000 / portTICK_PERIOD_MS;
bool read(uint8_t* data, size_t dataSize, TickType_t timeout = DEFAULT_TIMEOUT);
bool write(const uint8_t* data, uint16_t dataSize, TickType_t timeout = DEFAULT_TIMEOUT);
bool writeRead(const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout = DEFAULT_TIMEOUT);
bool readRegister8(uint8_t reg, uint8_t& result) const;
bool writeRegister(uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout = DEFAULT_TIMEOUT);
bool writeRegister8(uint8_t reg, uint8_t value) const;
bool readRegister12(uint8_t reg, float& out) const;
bool readRegister14(uint8_t reg, float& out) const;
bool readRegister16(uint8_t reg, uint16_t& out) const;
bool bitOn(uint8_t reg, uint8_t bitmask) const;
bool bitOff(uint8_t reg, uint8_t bitmask) const;
bool bitOnByIndex(uint8_t reg, uint8_t index) const { return bitOn(reg, 1 << index); }
bool bitOffByIndex(uint8_t reg, uint8_t index) const { return bitOff(reg, 1 << index); }
public:
explicit I2cDevice(::Device* controller, uint32_t address) : controller(controller), address(address) {}
Type getType() const override { return Type::I2c; }
::Device* getController() const { return controller; }
uint8_t getAddress() const { return address; }
};
} // namespace tt::hal::i2c

View File

@ -1,27 +0,0 @@
#pragma once
#include <tactility/hal/Device.h>
#include <lvgl.h>
namespace tt::hal::keyboard {
class Display;
class KeyboardDevice : public Device {
public:
Type getType() const override { return Type::Keyboard; }
virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stopLvgl() = 0;
/** @return true when the keyboard currently is physically attached */
virtual bool isAttached() const = 0;
/** Could return nullptr if not started */
virtual lv_indev_t* getLvglIndev() = 0;
};
}

View File

@ -1,63 +0,0 @@
#pragma once
#include <tactility/hal/Device.h>
#include <cstdint>
#include <string>
namespace tt::hal::power {
class PowerDevice : public Device {
public:
PowerDevice();
~PowerDevice() override;
Type getType() const override { return Type::Power; }
enum class MetricType {
IsCharging, // bool
Current, // int32_t, mAh - battery current: either during charging (positive value) or discharging (negative value)
BatteryVoltage, // uint32_t, mV
ChargeLevel, // uint8_t [0, 100]
};
union MetricData {
int32_t valueAsInt32 = 0;
uint32_t valueAsUint32;
uint8_t valueAsUint8;
float valueAsFloat;
bool valueAsBool;
};
virtual bool supportsMetric(MetricType type) const = 0;
/**
* @return false when metric is not supported or (temporarily) not available.
*/
virtual bool getMetric(MetricType type, MetricData& data) = 0;
virtual bool supportsChargeControl() const { return false; }
virtual bool isAllowedToCharge() const { return false; }
virtual void setAllowedToCharge(bool canCharge) { /* NO-OP*/ }
virtual bool supportsQuickCharge() const { return false; }
virtual bool isQuickChargeEnabled() const { return false; }
virtual void setQuickChargeEnabled(bool enabled) { /* NO-OP */ }
virtual bool supportsPowerOff() const { return false; }
virtual void powerOff() { /* NO-OP*/ }
private:
/** Creates the kernel-level power_supply device that exposes this instance to TactilityKernel. */
void createPowerSupplyDevice();
/** Destroys the kernel-level power_supply device created by createPowerSupplyDevice(). */
void destroyPowerSupplyDevice();
std::string kernelDeviceName;
KernelDevice kernelDevice {};
};
}

View File

@ -1,22 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <Tactility/hal/touch/TouchDriver.h>
#include <tactility/device.h>
namespace tt::hal::touch {
/** Wraps a TactilityKernel Device of type POINTER_TYPE as a TouchDriver. */
class KernelTouchDriver final : public TouchDriver {
::Device* device;
public:
explicit KernelTouchDriver(::Device* device);
bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) override;
};
}

View File

@ -1,36 +0,0 @@
#pragma once
#include <tactility/hal/Device.h>
#include "TouchDriver.h"
#include <lvgl.h>
namespace tt::hal::touch {
class Display;
class TouchDevice : public Device {
public:
Type getType() const override { return Type::Touch; }
virtual bool start() = 0;
virtual bool stop() = 0;
virtual bool supportsLvgl() const = 0;
virtual bool startLvgl(lv_display_t* display) = 0;
virtual bool stopLvgl() = 0;
/** Could return nullptr if not started */
virtual lv_indev_t* getLvglIndev() = 0;
virtual bool supportsTouchDriver() = 0;
virtual bool supportsCalibration() const { return false; }
/** Could return nullptr if not supported */
virtual std::shared_ptr<TouchDriver> getTouchDriver() = 0;
};
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <cstdint>
namespace tt::hal::touch {
class TouchDriver {
public:
/**
* Get the coordinates for the currently touched points on the screen.
*
* @param[in] x array of X coordinates
* @param[in] y array of Y coordinates
* @param[in] strength optional array of strengths (nullable)
* @param[in] pointCount the number of points currently touched on the screen
* @param[in] maxPointCount the maximum number of points that can be touched at once
*
* @return true when touched and coordinates are available
*/
virtual bool getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) = 0;
};
}

View File

@ -1,9 +0,0 @@
#pragma once
#include "Tactility/hal/Configuration.h"
namespace tt::hal {
void init(const Configuration& configuration);
} // namespace

View File

@ -1,11 +0,0 @@
#pragma once
#include <Tactility/lvgl/Lvgl.h>
namespace tt::lvgl {
void attachDevices();
void detachDevices();
}

View File

@ -14,8 +14,6 @@
#include <Tactility/app/AppRegistration.h>
#include <Tactility/file/File.h>
#include <Tactility/file/FileLock.h>
#include <Tactility/hal/HalPrivate.h>
#include <Tactility/lvgl/LvglPrivate.h>
#include <Tactility/network/NtpPrivate.h>
#include <Tactility/service/ServiceManifest.h>
#include <Tactility/service/ServiceRegistration.h>
@ -41,6 +39,8 @@
#endif
#include "Tactility/Paths.h"
#include "Tactility/SystemEvents.h"
#include "Tactility/hal/SdCard.h"
#include <Tactility/bluetooth/Bluetooth.h>
@ -49,7 +49,6 @@ namespace tt {
constexpr auto* TAG = "Tactility";
static const Configuration* config_instance = nullptr;
static DispatcherHandle_t mainDispatcherHandle = dispatcher_alloc();
namespace {
@ -183,8 +182,6 @@ static void registerInternalApps() {
}
if (device_exists_of_type(&DISPLAY_TYPE)) {
addAppManifest(app::kerneldisplay::manifest);
} else if (hal::hasDevice(hal::Device::Type::Display)) {
addAppManifest(app::display::manifest);
}
addAppManifest(app::files::manifest);
addAppManifest(app::fileselection::manifest);
@ -366,11 +363,9 @@ void registerApps() {
registerInstalledAppsFromFileSystems();
}
void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices[]) {
void run(Module* dtsModules[], DtsDevice dtsDevices[]) {
LOG_I(TAG, "Tactility v%s on %s (%s)", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID);
assert(config.hardware);
LOG_I(TAG, "Initializing kernel");
if (kernel_init(dtsModules, dtsDevices) != ERROR_NONE) {
LOG_E(TAG, "Failed to initialize kernel");
@ -383,23 +378,25 @@ void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices
// crypt-module
check(module_construct_add_start(&crypt_module) == ERROR_NONE);
// Assign early so starting services can use it
config_instance = &config;
#ifdef ESP_PLATFORM
initEsp();
#endif
file::setFindLockFunction(file::findLock);
settings::initTimeZone();
hal::init(*config.hardware);
// Remnants of the old HAL
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin);
hal::sdcard::mountAll();
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd);
network::ntp::init();
bluetooth::systemStart();
registerAndStartPrimaryServices();
lvgl_module_configure((LvglModuleConfig) {
.on_start = lvgl::attachDevices,
.on_stop = lvgl::detachDevices,
.on_start = nullptr,
.on_stop = nullptr,
.task_priority = THREAD_PRIORITY_HIGHER,
/** Minimum seems to be about 3500. In some scenarios, the WiFi app crashes at 8192,
* so we now have 9120 to run in a stable manner. We should figure out a way to avoid this.
@ -411,7 +408,7 @@ void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices
});
check(module_construct(&lvgl_module) == ERROR_NONE);
check(module_add(&lvgl_module) == ERROR_NONE);
lvgl::start();
module_start(&lvgl_module);
registerAndStartSecondaryServices();
@ -428,11 +425,6 @@ void run(const Configuration& config, Module* dtsModules[], DtsDevice dtsDevices
}
}
/** return the configuration or nullptr if it's not initialized */
const Configuration* getConfiguration() {
return config_instance;
}
MainDispatcher getMainDispatcher() {
return MainDispatcher(mainDispatcherHandle);
}

View File

@ -9,7 +9,6 @@
#include <Tactility/app/AppContext.h>
#include <Tactility/app/AppPaths.h>
#include <Tactility/app/alertdialog/AlertDialog.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/usb/Usb.h>
#include <Tactility/lvgl/Style.h>
#include <Tactility/service/loader/Loader.h>
@ -36,10 +35,6 @@ constexpr auto* TAG = "Boot";
extern const AppManifest manifest;
static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() {
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
}
class BootApp : public App {
// Snapshot of hal::usb::isUsbBootMode(), taken before the boot thread starts and
@ -58,31 +53,7 @@ class BootApp : public App {
getCpuAffinityConfiguration().system
);
static void setupHalDisplay() {
const auto hal_display = getHalDisplay();
if (hal_display == nullptr) {
return;
}
settings::display::DisplaySettings settings;
if (settings::display::load(settings)) {
if (hal_display->getGammaCurveCount() > 0) {
hal_display->setGammaCurve(settings.gammaCurve);
LOG_I(TAG, "Gamma curve %d", settings.gammaCurve);
}
} else {
settings = settings::display::getDefault();
}
if (hal_display->supportsBacklightDuty()) {
LOG_I(TAG, "Backlight %d", settings.backlightDuty);
hal_display->setBacklightDuty(settings.backlightDuty);
} else {
LOG_I(TAG, "No backlight");
}
}
static void setupKernelDisplay() {
static void setupDisplay() {
auto* display = device_find_first_by_type(&DISPLAY_TYPE);
// Boards not yet migrated to the kernel display driver register a placeholder device (so
// the devicetree node resolves) with a NULL api - nothing for this function to act on.
@ -161,8 +132,8 @@ class BootApp : public App {
// TODO: Support for multiple displays
LOG_I(TAG, "Setup display");
setupHalDisplay();
setupKernelDisplay();
setupDisplay();
LOG_I(TAG, "Prepare file systems");
prepareFileSystems();
#ifdef CONFIG_TT_USER_DATA_LOCATION_SD

View File

@ -1,9 +1,8 @@
#ifdef ESP_PLATFORM
#include <Tactility/Timer.h>
#include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
#include <Tactility/app/AppManifest.h>
#include <Tactility/lvgl/Lvgl.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/lvgl/Style.h>
#include <Tactility/lvgl/Toolbar.h>
@ -14,6 +13,7 @@
#include <tactility/log.h>
#include <tactility/lvgl_icon_shared.h>
#include <tactility/lvgl_module.h>
#include <cstring>
#include <lvgl.h>
@ -33,7 +33,7 @@ class DevelopmentApp final : public App {
Timer timer = Timer(Timer::Type::Periodic, pdMS_TO_TICKS(1000), [this] {
auto lock = lvgl::getSyncLock()->asScopedLock();
// TODO: There's a crash when this is called when the app is being destroyed
if (lock.lock(lvgl::defaultLockTime) && lvgl::isStarted()) {
if (lock.lock(lvgl::defaultLockTime) && module_is_started(&lvgl_module)) {
updateViewState();
}
});

View File

@ -1,310 +0,0 @@
#include <Tactility/Tactility.h>
#include <tactility/lvgl_icon_shared.h>
#ifdef ESP_PLATFORM
#include <Tactility/service/displayidle/DisplayIdleService.h>
#endif
#include <Tactility/app/App.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/lvgl/Toolbar.h>
#include <Tactility/settings/DisplaySettings.h>
#include <lvgl.h>
#include <tactility/log.h>
#include <tactility/lvgl_module.h>
namespace tt::app::display {
constexpr auto* TAG = "Display";
static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() {
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
}
class HalDisplayApp final : public App {
settings::display::DisplaySettings displaySettings;
bool displaySettingsUpdated = false;
lv_obj_t* timeoutSwitch = nullptr;
lv_obj_t* timeoutDropdown = nullptr;
lv_obj_t* screensaverDropdown = nullptr;
static void onBacklightSliderEvent(lv_event_t* event) {
auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event));
auto* app = static_cast<HalDisplayApp*>(lv_event_get_user_data(event));
auto hal_display = getHalDisplay();
assert(hal_display != nullptr);
if (hal_display->supportsBacklightDuty()) {
int32_t slider_value = lv_slider_get_value(slider);
app->displaySettings.backlightDuty = static_cast<uint8_t>(slider_value);
app->displaySettingsUpdated = true;
hal_display->setBacklightDuty(app->displaySettings.backlightDuty);
}
}
static void onGammaSliderEvent(lv_event_t* event) {
auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event));
auto hal_display = hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
auto* app = static_cast<HalDisplayApp*>(lv_event_get_user_data(event));
assert(hal_display != nullptr);
if (hal_display->getGammaCurveCount() > 0) {
int32_t slider_value = lv_slider_get_value(slider);
app->displaySettings.gammaCurve = static_cast<uint8_t>(slider_value);
app->displaySettingsUpdated = true;
hal_display->setGammaCurve(app->displaySettings.gammaCurve);
}
}
static void onOrientationSet(lv_event_t* event) {
auto* app = static_cast<HalDisplayApp*>(lv_event_get_user_data(event));
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
uint32_t selected_index = lv_dropdown_get_selected(dropdown);
LOG_I(TAG, "Selected %u", (unsigned)selected_index);
auto selected_orientation = static_cast<settings::display::Orientation>(selected_index);
if (selected_orientation != app->displaySettings.orientation) {
app->displaySettings.orientation = selected_orientation;
app->displaySettingsUpdated = true;
lv_display_set_rotation(lv_display_get_default(), settings::display::toLvglDisplayRotation(selected_orientation));
}
}
static void onTimeoutSwitch(lv_event_t* event) {
auto* app = static_cast<HalDisplayApp*>(lv_event_get_user_data(event));
auto* sw = static_cast<lv_obj_t*>(lv_event_get_target(event));
bool enabled = lv_obj_has_state(sw, LV_STATE_CHECKED);
app->displaySettings.backlightTimeoutEnabled = enabled;
app->displaySettingsUpdated = true;
if (app->timeoutDropdown) {
if (enabled) {
lv_obj_clear_state(app->timeoutDropdown, LV_STATE_DISABLED);
if (app->screensaverDropdown) {
lv_obj_clear_state(app->screensaverDropdown, LV_STATE_DISABLED);
}
} else {
lv_obj_add_state(app->timeoutDropdown, LV_STATE_DISABLED);
if (app->screensaverDropdown) {
lv_obj_add_state(app->screensaverDropdown, LV_STATE_DISABLED);
}
}
}
}
static void onTimeoutChanged(lv_event_t* event) {
auto* app = static_cast<HalDisplayApp*>(lv_event_get_user_data(event));
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
uint32_t idx = lv_dropdown_get_selected(dropdown);
// Map dropdown index to ms: 0=15s,1=30s,2=1m,3=2m,4=5m,5=Never
static const uint32_t values_ms[] = {15000, 30000, 60000, 120000, 300000, 0};
if (idx < (sizeof(values_ms)/sizeof(values_ms[0]))) {
app->displaySettings.backlightTimeoutMs = values_ms[idx];
app->displaySettingsUpdated = true;
}
}
static void onScreensaverChanged(lv_event_t* event) {
auto* app = static_cast<HalDisplayApp*>(lv_event_get_user_data(event));
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
uint32_t idx = lv_dropdown_get_selected(dropdown);
// Validate index bounds before casting to enum
if (idx >= static_cast<uint32_t>(settings::display::ScreensaverType::Count)) {
return;
}
auto selected_type = static_cast<settings::display::ScreensaverType>(idx);
if (selected_type != app->displaySettings.screensaverType) {
app->displaySettings.screensaverType = selected_type;
app->displaySettingsUpdated = true;
}
}
public:
void onShow(AppContext& app, lv_obj_t* parent) override {
displaySettings = settings::display::loadOrGetDefault();
auto ui_density = lvgl_get_ui_density();
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
auto hal_display = getHalDisplay();
assert(hal_display != nullptr);
lvgl::toolbar_create(parent, app);
auto* main_wrapper = lv_obj_create(parent);
lv_obj_set_flex_flow(main_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_width(main_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(main_wrapper, 1);
// Backlight slider
if (hal_display->supportsBacklightDuty()) {
auto* brightness_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(brightness_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_hor(brightness_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(brightness_wrapper, 0, LV_STATE_DEFAULT);
if (ui_density != LVGL_UI_DENSITY_COMPACT) {
lv_obj_set_style_pad_ver(brightness_wrapper, 4, LV_STATE_DEFAULT);
}
auto* brightness_label = lv_label_create(brightness_wrapper);
lv_label_set_text(brightness_label, "Brightness");
lv_obj_align(brightness_label, LV_ALIGN_LEFT_MID, 0, 0);
auto* brightness_slider = lv_slider_create(brightness_wrapper);
lv_obj_set_width(brightness_slider, LV_PCT(50));
lv_obj_align(brightness_slider, LV_ALIGN_RIGHT_MID, 0, 0);
lv_slider_set_range(brightness_slider, 0, 255);
lv_obj_add_event_cb(brightness_slider, onBacklightSliderEvent, LV_EVENT_VALUE_CHANGED, this);
lv_slider_set_value(brightness_slider, displaySettings.backlightDuty, LV_ANIM_OFF);
}
// Gamma slider
if (hal_display->getGammaCurveCount() > 0) {
auto* gamma_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(gamma_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_hor(gamma_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(gamma_wrapper, 0, LV_STATE_DEFAULT);
if (ui_density != LVGL_UI_DENSITY_COMPACT) {
lv_obj_set_style_pad_ver(gamma_wrapper, 4, LV_STATE_DEFAULT);
}
auto* gamma_label = lv_label_create(gamma_wrapper);
lv_label_set_text(gamma_label, "Gamma");
lv_obj_align(gamma_label, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_set_y(gamma_label, 0);
auto* gamma_slider = lv_slider_create(gamma_wrapper);
lv_obj_set_width(gamma_slider, LV_PCT(50));
lv_obj_align(gamma_slider, LV_ALIGN_RIGHT_MID, 0, 0);
lv_slider_set_range(gamma_slider, 0, hal_display->getGammaCurveCount());
lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, this);
uint8_t curve_index = displaySettings.gammaCurve;
lv_slider_set_value(gamma_slider, curve_index, LV_ANIM_OFF);
}
// Orientation
auto* orientation_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(orientation_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(orientation_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(orientation_wrapper, 0, LV_STATE_DEFAULT);
auto* orientation_label = lv_label_create(orientation_wrapper);
lv_label_set_text(orientation_label, "Orientation");
lv_obj_align(orientation_label, LV_ALIGN_LEFT_MID, 0, 0);
auto* orientation_dropdown = lv_dropdown_create(orientation_wrapper);
// Note: order correlates with settings::display::Orientation item order
lv_dropdown_set_options(orientation_dropdown, "Landscape\nPortrait Right\nLandscape Flipped\nPortrait Left");
lv_obj_align(orientation_dropdown, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(orientation_dropdown, onOrientationSet, LV_EVENT_VALUE_CHANGED, this);
// Set the dropdown to match current orientation enum
lv_dropdown_set_selected(orientation_dropdown, static_cast<uint16_t>(displaySettings.orientation));
// Screen timeout
if (hal_display->supportsBacklightDuty()) {
auto* timeout_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(timeout_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(timeout_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(timeout_wrapper, 0, LV_STATE_DEFAULT);
auto* timeout_label = lv_label_create(timeout_wrapper);
lv_label_set_text(timeout_label, "Auto screen off");
lv_obj_align(timeout_label, LV_ALIGN_LEFT_MID, 0, 0);
timeoutSwitch = lv_switch_create(timeout_wrapper);
if (displaySettings.backlightTimeoutEnabled) {
lv_obj_add_state(timeoutSwitch, LV_STATE_CHECKED);
}
lv_obj_align(timeoutSwitch, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(timeoutSwitch, onTimeoutSwitch, LV_EVENT_VALUE_CHANGED, this);
auto* timeout_select_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(timeout_select_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(timeout_select_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(timeout_select_wrapper, 0, LV_STATE_DEFAULT);
auto* timeout_value_label = lv_label_create(timeout_select_wrapper);
lv_label_set_text(timeout_value_label, "Timeout");
lv_obj_align(timeout_value_label, LV_ALIGN_LEFT_MID, 0, 0);
timeoutDropdown = lv_dropdown_create(timeout_select_wrapper);
lv_dropdown_set_options(timeoutDropdown, "15 seconds\n30 seconds\n1 minute\n2 minutes\n5 minutes\nNever");
lv_obj_align(timeoutDropdown, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(timeoutDropdown, onTimeoutChanged, LV_EVENT_VALUE_CHANGED, this);
// Initialize dropdown selection from settings
uint32_t ms = displaySettings.backlightTimeoutMs;
uint32_t idx = 2; // default 1 minute
if (ms == 15000) idx = 0;
else if (ms == 30000)
idx = 1;
else if (ms == 60000)
idx = 2;
else if (ms == 120000)
idx = 3;
else if (ms == 300000)
idx = 4;
else if (ms == 0)
idx = 5;
lv_dropdown_set_selected(timeoutDropdown, idx);
if (!displaySettings.backlightTimeoutEnabled) {
lv_obj_add_state(timeoutDropdown, LV_STATE_DISABLED);
}
// Screensaver type
auto* screensaver_wrapper = lv_obj_create(main_wrapper);
lv_obj_set_size(screensaver_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(screensaver_wrapper, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(screensaver_wrapper, 0, LV_STATE_DEFAULT);
auto* screensaver_label = lv_label_create(screensaver_wrapper);
lv_label_set_text(screensaver_label, "Screensaver");
lv_obj_align(screensaver_label, LV_ALIGN_LEFT_MID, 0, 0);
screensaverDropdown = lv_dropdown_create(screensaver_wrapper);
// Note: order correlates with settings::display::ScreensaverType enum order
lv_dropdown_set_options(screensaverDropdown, "None\nBouncing Balls\nMystify\nMatrix Rain\nStackChan");
lv_obj_align(screensaverDropdown, LV_ALIGN_RIGHT_MID, 0, 0);
lv_obj_add_event_cb(screensaverDropdown, onScreensaverChanged, LV_EVENT_VALUE_CHANGED, this);
lv_dropdown_set_selected(screensaverDropdown, static_cast<uint16_t>(displaySettings.screensaverType));
if (!displaySettings.backlightTimeoutEnabled) {
lv_obj_add_state(screensaverDropdown, LV_STATE_DISABLED);
}
}
}
void onHide(AppContext& app) override {
if (displaySettingsUpdated) {
// 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);
#ifdef ESP_PLATFORM
// Notify DisplayIdle service to reload settings
auto displayIdle = service::displayidle::findService();
if (displayIdle) {
displayIdle->reloadSettings();
}
#endif
});
}
}
};
extern const AppManifest manifest = {
.appId = "Display",
.appName = "Display",
.appIcon = LVGL_ICON_SHARED_DISPLAY_SETTINGS,
.appCategory = Category::Settings,
.createApp = create<HalDisplayApp>
};
} // namespace

View File

@ -4,13 +4,11 @@
#include <Tactility/app/AppContext.h>
#include <Tactility/app/AppRegistration.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/service/loader/Loader.h>
#include <lvgl.h>
#include <tactility/device.h>
#include <tactility/drivers/power_supply.h>
#include <tactility/hal/Device.h>
#include <tactility/lvgl_fonts.h>
#include <tactility/lvgl_icon_shared.h>

View File

@ -1,4 +1,4 @@
#include "tactility/lvgl_module.h"
#include <tactility/lvgl_module.h>
#include <Tactility/RecursiveMutex.h>

View File

@ -1,83 +0,0 @@
#include <Tactility/SystemEvents.h>
#include <Tactility/Tactility.h>
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/SdCard.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/touch/TouchDevice.h>
#include <tactility/check.h>
#include <tactility/hal/Device.h>
#include <tactility/log.h>
namespace tt::hal {
constexpr auto* TAG = "Hal";
void registerDevices(const Configuration& configuration) {
LOG_I(TAG, "Registering devices");
auto devices = configuration.createDevices();
for (auto& device : devices) {
registerDevice(device);
// Register attached devices
if (device->getType() == Device::Type::Display) {
const auto display = std::static_pointer_cast<display::DisplayDevice>(device);
assert(display != nullptr);
const std::shared_ptr<Device> touch = display->getTouchDevice();
if (touch != nullptr) {
registerDevice(touch);
}
}
}
}
static void startDisplays() {
LOG_I(TAG, "Starting displays & touch");
auto displays = hal::findDevices<display::DisplayDevice>(Device::Type::Display);
for (auto& display : displays) {
LOG_I(TAG, "%s starting", display->getName().c_str());
if (!display->start()) {
LOG_E(TAG, "%s start failed", display->getName().c_str());
} else {
LOG_I(TAG, "%s started", display->getName().c_str());
if (display->supportsBacklightDuty()) {
LOG_I(TAG, "Setting backlight");
display->setBacklightDuty(0);
}
auto touch = display->getTouchDevice();
if (touch != nullptr) {
LOG_I(TAG, "%s starting", touch->getName().c_str());
if (!touch->start()) {
LOG_E(TAG, "%s start failed", touch->getName().c_str());
} else {
LOG_I(TAG, "%s started", touch->getName().c_str());
}
}
}
}
}
void init(const Configuration& configuration) {
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalBegin);
if (configuration.initBoot != nullptr) {
check(configuration.initBoot(), "Init boot failed");
}
registerDevices(configuration);
sdcard::mountAll(); // Warning: This needs to happen BEFORE displays are initialized on the SPI bus
startDisplays(); // Warning: SPI displays need to start after SPI SD cards are mounted
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd);
}
const Configuration* getConfiguration() {
return tt::getConfiguration()->hardware;
}
} // namespace

View File

@ -1,59 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
#include <Tactility/hal/display/KernelDisplayDriver.h>
#include <tactility/drivers/display.h>
#include <algorithm>
#include <cassert>
#include <utility>
namespace tt::hal::display {
static ColorFormat toColorFormat(DisplayColorFormat format) {
switch (format) {
case DISPLAY_COLOR_FORMAT_MONOCHROME:
return ColorFormat::Monochrome;
case DISPLAY_COLOR_FORMAT_BGR565:
return ColorFormat::BGR565;
case DISPLAY_COLOR_FORMAT_BGR565_SWAPPED:
return ColorFormat::BGR565Swapped;
case DISPLAY_COLOR_FORMAT_RGB565:
return ColorFormat::RGB565;
case DISPLAY_COLOR_FORMAT_RGB565_SWAPPED:
return ColorFormat::RGB565Swapped;
case DISPLAY_COLOR_FORMAT_RGB888:
return ColorFormat::RGB888;
default:
std::unreachable();
}
}
KernelDisplayDriver::KernelDisplayDriver(::Device* device) : device(device) {
assert(device_get_type(device) == &DISPLAY_TYPE);
}
ColorFormat KernelDisplayDriver::getColorFormat() const {
return toColorFormat(display_get_color_format(device));
}
uint16_t KernelDisplayDriver::getPixelWidth() const {
return display_get_resolution_x(device);
}
uint16_t KernelDisplayDriver::getPixelHeight() const {
return display_get_resolution_y(device);
}
bool KernelDisplayDriver::drawBitmap(int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) {
return display_draw_bitmap(device, xStart, yStart, xEnd, yEnd, pixelData) == ERROR_NONE;
}
uint8_t KernelDisplayDriver::getFrameBuffers(void* outBuffers[2]) const {
uint8_t count = std::min<uint8_t>(display_get_frame_buffer_count(device), 2);
for (uint8_t i = 0; i < count; i++) {
display_get_frame_buffer(device, i, &outBuffers[i]);
}
return count;
}
}

View File

@ -1,87 +0,0 @@
#include <Tactility/hal/gpio/Gpio.h>
#ifdef ESP_PLATFORM
#include <driver/gpio.h>
#endif
namespace tt::hal::gpio {
#ifdef ESP_PLATFORM
constexpr gpio_num_t toEspPin(Pin pin) { return static_cast<gpio_num_t>(pin); }
constexpr gpio_mode_t toEspGpioMode(Mode mode) {
switch (mode) {
case Mode::Input:
return GPIO_MODE_INPUT;
case Mode::Output:
return GPIO_MODE_OUTPUT;
case Mode::OutputOpenDrain:
return GPIO_MODE_OUTPUT_OD;
case Mode::InputOutput:
return GPIO_MODE_INPUT_OUTPUT;
case Mode::InputOutputOpenDrain:
return GPIO_MODE_INPUT_OUTPUT_OD;
case Mode::Disable:
default:
return GPIO_MODE_DISABLE;
}
}
#endif
bool getLevel(Pin pin) {
#ifdef ESP_PLATFORM
return gpio_get_level(toEspPin(pin)) == 1;
#else
return false;
#endif
}
bool setLevel(Pin pin, bool level) {
#ifdef ESP_PLATFORM
return gpio_set_level(toEspPin(pin), level) == ESP_OK;
#else
return true;
#endif
}
int getPinCount() {
#ifdef ESP_PLATFORM
return GPIO_NUM_MAX;
#else
return 16;
#endif
}
bool configureWithPinBitmask(uint64_t pinBitMask, Mode mode, bool pullUp, bool pullDown) {
#ifdef ESP_PLATFORM
gpio_config_t sd_gpio_config = {
.pin_bit_mask = pinBitMask,
.mode = toEspGpioMode(mode),
.pull_up_en = pullUp ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.pull_down_en = pullDown ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
return gpio_config(&sd_gpio_config) == ESP_OK;
#else
return true;
#endif
}
bool configure(Pin pin, Mode mode, bool pullUp, bool pullDown) {
#ifdef ESP_PLATFORM
return configureWithPinBitmask(BIT64(toEspPin(pin)), mode, pullUp, pullDown);
#else
return true;
#endif
}
bool setMode(Pin pin, Mode mode) {
#ifdef ESP_PLATFORM
return gpio_set_direction(toEspPin(pin), toEspGpioMode(mode)) == ESP_OK;
#endif
return true;
}
}

View File

@ -2,8 +2,6 @@
#include "Tactility/service/gps/GpsService.h"
#include "Tactility/file/ObjectFile.h"
#include <Tactility/TactilityCore.h>
namespace tt::hal::gps {
const char* toString(GpsModel model) {

View File

@ -17,34 +17,27 @@ constexpr auto* TAG = "GpsDevice";
int32_t GpsDevice::threadMain() {
uint8_t buffer[GPS_UART_BUFFER_SIZE];
auto* uart = device_find_by_name(configuration.uartName);
if (uart == nullptr) {
LOG_E(TAG, "Failed to find UART %s", configuration.uartName);
return -1;
}
struct UartConfig uartConfig = {
.baud_rate = configuration.baudRate,
UartConfig uartConfig = {
.baud_rate = baudRate,
.data_bits = UART_CONTROLLER_DATA_8_BITS,
.parity = UART_CONTROLLER_PARITY_DISABLE,
.stop_bits = UART_CONTROLLER_STOP_BITS_1
};
error_t error = uart_controller_set_config(uart, &uartConfig);
error_t error = uart_controller_set_config(uartDevice, &uartConfig);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to configure UART %s: %s", configuration.uartName, error_to_string(error));
LOG_E(TAG, "Failed to configure UART %s: %s", uartDevice->name, error_to_string(error));
return -1;
}
error = uart_controller_open(uart);
error = uart_controller_open(uartDevice);
if (error != ERROR_NONE) {
LOG_E(TAG, "Failed to open UART %s: %s", configuration.uartName, error_to_string(error));
LOG_E(TAG, "Failed to open UART %s: %s", uartDevice->name, error_to_string(error));
return -1;
}
GpsModel model = configuration.model;
if (model == GpsModel::Unknown) {
model = probe(uart);
model = probe(uartDevice);
if (model == GpsModel::Unknown) {
LOG_E(TAG, "Probe failed");
setState(State::Error);
@ -52,10 +45,9 @@ int32_t GpsDevice::threadMain() {
}
}
mutex.lock();
this->model = model;
mutex.unlock();
if (!init(uart, model)) {
if (!init(uartDevice, model)) {
LOG_E(TAG, "Init failed");
setState(State::Error);
return -1;
@ -66,7 +58,7 @@ int32_t GpsDevice::threadMain() {
// Reference: https://gpsd.gitlab.io/gpsd/NMEA.html
while (!isThreadInterrupted()) {
size_t bytes_read = 0;
uart_controller_read_until(uart, buffer, GPS_UART_BUFFER_SIZE, '\n', true, &bytes_read, 100 / portTICK_PERIOD_MS);
uart_controller_read_until(uartDevice, buffer, GPS_UART_BUFFER_SIZE, '\n', true, &bytes_read, 100 / portTICK_PERIOD_MS);
// Thread might've been interrupted in the meanwhile
if (isThreadInterrupted()) {
@ -110,8 +102,8 @@ int32_t GpsDevice::threadMain() {
}
}
if (uart_controller_close(uart) != ERROR_NONE) {
LOG_W(TAG, "Failed to stop UART %s", configuration.uartName);
if (uart_controller_close(uartDevice) != ERROR_NONE) {
LOG_W(TAG, "Failed to stop UART %s", uartDevice->name);
}
return 0;

View File

@ -1,81 +0,0 @@
#include "Tactility/hal/i2c/I2cDevice.h"
#include <cstdint>
namespace tt::hal::i2c {
bool I2cDevice::read(uint8_t* data, size_t dataSize, TickType_t timeout) {
return i2c_controller_read(controller, address, data, dataSize, timeout) == ERROR_NONE;
}
bool I2cDevice::write(const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
return i2c_controller_write(controller, address, data, dataSize, timeout) == ERROR_NONE;
}
bool I2cDevice::writeRead(const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) {
return i2c_controller_write_read(controller, address, writeData, writeDataSize, readData, readDataSize, timeout) == ERROR_NONE;
}
bool I2cDevice::writeRegister(uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) {
return i2c_controller_write_register(controller, address, reg, data, dataSize, timeout) == ERROR_NONE;
}
bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
std::uint8_t data[2] = {0};
if (i2c_controller_read_register(controller, address, reg, data, 2, DEFAULT_TIMEOUT) == ERROR_NONE) {
out = (data[0] & 0x0F) << 8 | data[1];
return true;
} else {
return false;
}
}
bool I2cDevice::readRegister14(uint8_t reg, float& out) const {
std::uint8_t data[2] = {0};
if (i2c_controller_read_register(controller, address, reg, data, 2, DEFAULT_TIMEOUT) == ERROR_NONE) {
out = (data[0] & 0x3F) << 8 | data[1];
return true;
} else {
return false;
}
}
bool I2cDevice::readRegister16(uint8_t reg, uint16_t& out) const {
std::uint8_t data[2] = {0};
if (i2c_controller_read_register(controller, address, reg, data, 2, DEFAULT_TIMEOUT) == ERROR_NONE) {
out = data[0] << 8 | data[1];
return true;
} else {
return false;
}
}
bool I2cDevice::readRegister8(uint8_t reg, uint8_t& result) const {
return i2c_controller_write_read(controller, address, &reg, 1, &result, 1, DEFAULT_TIMEOUT) == ERROR_NONE;
}
bool I2cDevice::writeRegister8(uint8_t reg, uint8_t value) const {
return i2c_controller_write_register(controller, address, reg, &value, 1, DEFAULT_TIMEOUT) == ERROR_NONE;
}
bool I2cDevice::bitOn(uint8_t reg, uint8_t bitmask) const {
uint8_t state;
if (readRegister8(reg, state)) {
state |= bitmask;
return writeRegister8(reg, state);
} else {
return false;
}
}
bool I2cDevice::bitOff(uint8_t reg, uint8_t bitmask) const {
uint8_t state;
if (readRegister8(reg, state)) {
state &= ~bitmask;
return writeRegister8(reg, state);
} else {
return false;
}
}
} // namespace tt::hal::i2c

View File

@ -1,172 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
#include <Tactility/hal/power/PowerDevice.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/driver.h>
#include <tactility/drivers/power_supply.h>
#include <format>
namespace tt::hal::power {
#define GET_POWER_DEVICE(device) (static_cast<PowerDevice*>(device_get_driver_data(device)))
static PowerDevice::MetricType toMetricType(PowerSupplyProperty property) {
switch (property) {
case POWER_SUPPLY_PROP_IS_CHARGING:
return PowerDevice::MetricType::IsCharging;
case POWER_SUPPLY_PROP_CURRENT:
return PowerDevice::MetricType::Current;
case POWER_SUPPLY_PROP_VOLTAGE:
return PowerDevice::MetricType::BatteryVoltage;
case POWER_SUPPLY_PROP_CAPACITY:
default:
return PowerDevice::MetricType::ChargeLevel;
}
}
static int toIntValue(PowerDevice::MetricType type, const PowerDevice::MetricData& data) {
switch (type) {
case PowerDevice::MetricType::IsCharging:
return data.valueAsBool ? 1 : 0;
case PowerDevice::MetricType::Current:
return data.valueAsInt32;
case PowerDevice::MetricType::BatteryVoltage:
return static_cast<int>(data.valueAsUint32);
case PowerDevice::MetricType::ChargeLevel:
default:
return data.valueAsUint8;
}
}
static bool apiSupportsProperty(::Device* device, PowerSupplyProperty property) {
return GET_POWER_DEVICE(device)->supportsMetric(toMetricType(property));
}
static error_t apiGetProperty(::Device* device, PowerSupplyProperty property, PowerSupplyPropertyValue* outValue) {
auto* power_device = GET_POWER_DEVICE(device);
auto metric_type = toMetricType(property);
if (!power_device->supportsMetric(metric_type)) {
return ERROR_NOT_SUPPORTED;
}
PowerDevice::MetricData data;
if (!power_device->getMetric(metric_type, data)) {
return ERROR_NOT_FOUND;
}
outValue->int_value = toIntValue(metric_type, data);
return ERROR_NONE;
}
static bool apiSupportsChargeControl(::Device* device) {
return GET_POWER_DEVICE(device)->supportsChargeControl();
}
static bool apiIsAllowedToCharge(::Device* device) {
return GET_POWER_DEVICE(device)->isAllowedToCharge();
}
static error_t apiSetAllowedToCharge(::Device* device, bool allowed) {
auto* power_device = GET_POWER_DEVICE(device);
if (!power_device->supportsChargeControl()) {
return ERROR_NOT_SUPPORTED;
}
power_device->setAllowedToCharge(allowed);
return ERROR_NONE;
}
static bool apiSupportsQuickCharge(::Device* device) {
return GET_POWER_DEVICE(device)->supportsQuickCharge();
}
static bool apiIsQuickChargeEnabled(::Device* device) {
return GET_POWER_DEVICE(device)->isQuickChargeEnabled();
}
static error_t apiSetQuickChargeEnabled(::Device* device, bool enabled) {
auto* power_device = GET_POWER_DEVICE(device);
if (!power_device->supportsQuickCharge()) {
return ERROR_NOT_SUPPORTED;
}
power_device->setQuickChargeEnabled(enabled);
return ERROR_NONE;
}
static bool apiSupportsPowerOff(::Device* device) {
return GET_POWER_DEVICE(device)->supportsPowerOff();
}
static error_t apiPowerOff(::Device* device) {
auto* power_device = GET_POWER_DEVICE(device);
if (!power_device->supportsPowerOff()) {
return ERROR_NOT_SUPPORTED;
}
power_device->powerOff();
return ERROR_NONE;
}
static error_t startDevice(::Device*) { return ERROR_NONE; }
static error_t stopDevice(::Device*) { return ERROR_NONE; }
static PowerSupplyApi powerSupplyApi {
.supports_property = apiSupportsProperty,
.get_property = apiGetProperty,
.supports_charge_control = apiSupportsChargeControl,
.is_allowed_to_charge = apiIsAllowedToCharge,
.set_allowed_to_charge = apiSetAllowedToCharge,
.supports_quick_charge = apiSupportsQuickCharge,
.is_quick_charge_enabled = apiIsQuickChargeEnabled,
.set_quick_charge_enabled = apiSetQuickChargeEnabled,
.supports_power_off = apiSupportsPowerOff,
.power_off = apiPowerOff,
};
static const char* powerSupplyCompatible[] = { "hal-power-device", nullptr };
static Driver powerSupplyDriver {
.name = "hal-power-device",
.compatible = powerSupplyCompatible,
.start_device = startDevice,
.stop_device = stopDevice,
.api = &powerSupplyApi,
.device_type = &POWER_SUPPLY_TYPE,
.owner = nullptr,
.internal = nullptr,
};
/** Registers the "hal-power-device" driver with the kernel on first use. */
static Driver& getPowerSupplyDriver() {
static const bool registered = [] {
check(driver_construct_add(&powerSupplyDriver) == ERROR_NONE);
return true;
}();
(void)registered;
return powerSupplyDriver;
}
void PowerDevice::createPowerSupplyDevice() {
kernelDeviceName = std::format("power-supply-{}", getId());
kernelDevice.name = kernelDeviceName.c_str();
check(device_construct(&kernelDevice) == ERROR_NONE);
check(device_add(&kernelDevice) == ERROR_NONE);
device_set_driver(&kernelDevice, &getPowerSupplyDriver());
check(device_start(&kernelDevice) == ERROR_NONE);
device_set_driver_data(&kernelDevice, this);
}
void PowerDevice::destroyPowerSupplyDevice() {
device_set_driver_data(&kernelDevice, nullptr);
check(device_stop(&kernelDevice) == ERROR_NONE);
check(device_remove(&kernelDevice) == ERROR_NONE);
check(device_destruct(&kernelDevice) == ERROR_NONE);
}
PowerDevice::PowerDevice() {
createPowerSupplyDevice();
}
PowerDevice::~PowerDevice() {
destroyPowerSupplyDevice();
}
}

View File

@ -1,24 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
#include <Tactility/hal/touch/KernelTouchDriver.h>
#include <tactility/drivers/pointer.h>
#include <cassert>
namespace tt::hal::touch {
// Bus reads are expected to complete quickly; bound the wait so a stalled controller can't block the LVGL indev poll.
static constexpr TickType_t READ_TIMEOUT = pdMS_TO_TICKS(10);
KernelTouchDriver::KernelTouchDriver(::Device* device) : device(device) {
assert(device_get_type(device) == &POINTER_TYPE);
}
bool KernelTouchDriver::getTouchedPoints(uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) {
if (pointer_read_data(device, READ_TIMEOUT) != ERROR_NONE) {
return false;
}
return pointer_get_touched_points(device, x, y, strength, pointCount, maxPointCount);
}
}

View File

@ -1,205 +0,0 @@
#include <Tactility/hal/Configuration.h>
#include <Tactility/hal/encoder/EncoderDevice.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/keyboard/KeyboardDevice.h>
#include <Tactility/hal/touch/TouchDevice.h>
#include <Tactility/lvgl/Keyboard.h>
#include <Tactility/lvgl/Lvgl.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/ServiceRegistration.h>
#include <Tactility/settings/DisplaySettings.h>
#include <Tactility/settings/TouchCalibrationSettings.h>
#include <tactility/device.h>
#include <tactility/drivers/backlight.h>
#include <tactility/log.h>
#include <tactility/lvgl_module.h>
#include <tactility/lvgl_pointer.h>
#include <tactility/module.h>
#include <lvgl.h>
namespace tt::lvgl {
constexpr auto* TAG = "LVGL";
bool isStarted() {
return module_is_started(&lvgl_module);
}
void attachDevices() {
LOG_I(TAG, "Adding devices");
auto lock = getSyncLock()->asScopedLock();
lock.lock();
// Start displays (their related touch devices start automatically within)
LOG_I(TAG, "Start displays");
auto hal_displays= hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
for (const auto& display: hal_displays) {
if (display->supportsLvgl()) {
if (display->startLvgl()) {
LOG_I(TAG, "Started %s", display->getName().c_str());
} else {
LOG_E(TAG, "Start failed for %s", display->getName().c_str());
}
}
}
auto* primary_lvgl_display = lv_disp_get_default();
if (primary_lvgl_display != nullptr) {
LOG_I(TAG, "Set default display rotation");
auto settings = settings::display::loadOrGetDefault();
lv_display_rotation_t rotation = settings::display::toLvglDisplayRotation(settings.orientation);
if (rotation != lv_display_get_rotation(primary_lvgl_display)) {
lv_display_set_rotation(primary_lvgl_display, rotation);
}
}
// Start touch
if (primary_lvgl_display != nullptr) {
LOG_I(TAG, "Start touch devices");
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (const auto& touch_device: touch_devices) {
// Start any touch devices that haven't been started yet
if (touch_device->supportsLvgl() && touch_device->getLvglIndev() == nullptr) {
if (touch_device->startLvgl(primary_lvgl_display)) {
LOG_I(TAG, "Started %s", touch_device->getName().c_str());
} else {
LOG_E(TAG, "Start failed for %s", touch_device->getName().c_str());
}
}
}
// Apply touch calibration (kernel POINTER_TYPE model only - see tactility/lvgl_pointer.h)
LOG_I(TAG, "Apply touch calibration");
auto touch_calibration_settings = settings::touch::loadOrGetDefault();
if (touch_calibration_settings.enabled && settings::touch::isValid(touch_calibration_settings)) {
auto* pointer_indev = lvgl_pointer_get_default();
if (pointer_indev != nullptr) {
struct LvglPointerCalibration calibration = {
.x_min = touch_calibration_settings.xMin,
.x_max = touch_calibration_settings.xMax,
.y_min = touch_calibration_settings.yMin,
.y_max = touch_calibration_settings.yMax,
};
if (lvgl_pointer_set_calibration(pointer_indev, &calibration) != ERROR_NONE) {
LOG_E(TAG, "Failed to apply saved touch calibration");
}
}
}
// Start keyboards
LOG_I(TAG, "Start keyboards");
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (const auto& keyboard: keyboards) {
if (keyboard->isAttached()) {
if (keyboard->startLvgl(primary_lvgl_display)) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
hardware_keyboard_set_indev(keyboard_indev);
LOG_I(TAG, "Started %s", keyboard->getName().c_str());
} else {
LOG_E(TAG, "Start failed for %s", keyboard->getName().c_str());
}
}
}
// Start encoders
LOG_I(TAG, "Start encoders");
auto encoders = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder);
for (const auto& encoder: encoders) {
if (encoder->startLvgl(primary_lvgl_display)) {
LOG_I(TAG, "Started %s", encoder->getName().c_str());
} else {
LOG_E(TAG, "Start failed for %s", encoder->getName().c_str());
}
}
}
// Restart services
// We search for the manifest first, because during the initial start() during boot
// the service won't be registered yet.
if (service::findManifestById("Gui") != nullptr) {
if (service::getState("Gui") == SERVICE_STATE_STOPPED) {
service::startService("Gui");
} else {
LOG_E(TAG, "Gui service is not in Stopped state");
}
}
// We search for the manifest first, because during the initial start() during boot
// the service won't be registered yet.
if (service::findManifestById("Statusbar") != nullptr) {
if (service::getState("Statusbar") == SERVICE_STATE_STOPPED) {
service::startService("Statusbar");
} else {
LOG_E(TAG, "Statusbar service is not in Stopped state");
}
}
}
void detachDevices() {
LOG_I(TAG, "Removing devices");
auto lock = getSyncLock()->asScopedLock();
lock.lock();
// Stop services that highly depend on LVGL
service::stopService("Statusbar");
service::stopService("Gui");
// Stop keyboards
LOG_I(TAG, "Stopping keyboards");
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (auto keyboard: keyboards) {
if (keyboard->getLvglIndev() != nullptr) {
keyboard->stopLvgl();
}
}
// Stop touch
LOG_I(TAG, "Stopping touch");
// The display generally stops their own touch devices, but we'll clean up anything that didn't
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device: touch_devices) {
if (touch_device->getLvglIndev() != nullptr) {
touch_device->stopLvgl();
}
}
// Stop encoders
LOG_I(TAG, "Stopping encoders");
// The display generally stops their own touch devices, but we'll clean up anything that didn't
auto encoder_devices = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder);
for (auto encoder_device: encoder_devices) {
if (encoder_device->getLvglIndev() != nullptr) {
encoder_device->stopLvgl();
}
}
// Stop displays (and their touch devices)
LOG_I(TAG, "Stopping displays");
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
for (auto display: displays) {
if (display->supportsLvgl() && display->getLvglDisplay() != nullptr && !display->stopLvgl()) {
LOG_E(TAG, "Failed to detach display from LVGL");
}
}
}
void start() {
check(module_start(&lvgl_module) == ERROR_NONE);
}
void stop() {
check(module_stop(&lvgl_module) == ERROR_NONE);
}
} // namespace

View File

@ -3,6 +3,8 @@
#include <Tactility/Tactility.h>
#include <Tactility/lvgl/Toolbar.h>
#include "tactility/drivers/pointer.h"
#include <Tactility/lvgl/Spinner.h>
#include <Tactility/service/loader/Loader.h>
@ -154,7 +156,7 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) {
// If we don't have a touch device, we assume there's some other kind of input like a keyboard, an encoder or button control
// In that scenario we want to automatically have the close button selected so the user doesn't have to press the widget selection
// an extra time for every screen.
if (!hal::hasDevice(hal::Device::Type::Touch)) {
if (!device_has_active_by_type(&POINTER_TYPE)) {
lv_group_focus_obj(toolbar->close_button);
}

View File

@ -2,32 +2,28 @@
#include <Tactility/service/displayidle/DisplayIdleService.h>
#include "Screensaver.h"
#include "BouncingBallsScreensaver.h"
#include "MatrixRainScreensaver.h"
#include "MystifyScreensaver.h"
#include "Screensaver.h"
#include "StackChanScreensaver.h"
#include <tactility/log.h>
#include <Tactility/CoreDefines.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/ServiceContext.h>
#include <Tactility/service/ServiceManifest.h>
#include <Tactility/service/ServiceRegistration.h>
#include <cstdlib>
#include <ctime>
#include <tactility/log.h>
#include <tactility/drivers/display.h>
#include <tactility/drivers/backlight.h>
#include <tactility/lvgl_module.h>
namespace tt::service::displayidle {
constexpr auto* TAG = "DisplayIdle";
constexpr uint32_t kWakeActivityThresholdMs = 100;
static std::shared_ptr<hal::display::DisplayDevice> getDisplay() {
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
}
void DisplayIdleService::stopScreensaverCb(lv_event_t* e) {
auto* self = static_cast<DisplayIdleService*>(lv_event_get_user_data(e));
lv_event_stop_bubbling(e);
@ -35,8 +31,34 @@ void DisplayIdleService::stopScreensaverCb(lv_event_t* e) {
lv_display_trigger_activity(nullptr);
}
static void setBacklightBrightness(uint8_t brightness) {
::Device* display;
if (device_get_first_active_by_type(&DISPLAY_TYPE, &display) == ERROR_NONE) {
::Device* backlight;
if (display_get_backlight(display, &backlight) == ERROR_NONE) {
device_get(backlight);
backlight_set_brightness(backlight, brightness);
device_put(backlight);
}
device_put(display);
}
}
static bool hasDisplayWithBacklight() {
::Device* display;
bool result = false;
if (device_get_first_active_by_type(&DISPLAY_TYPE, &display) == ERROR_NONE) {
::Device* backlight;
if (display_get_backlight(display, &backlight) == ERROR_NONE) {
result = true;
}
device_put(display);
}
return result;
}
void DisplayIdleService::stopScreensaver() {
if (!lvgl::lock(100)) {
if (!lvgl_try_lock(100)) {
// Lock failed - keep flag set to retry on next tick
return;
}
@ -52,7 +74,7 @@ void DisplayIdleService::stopScreensaver() {
lv_obj_delete(screensaverOverlay);
screensaverOverlay = nullptr;
}
lvgl::unlock();
lvgl_unlock();
stopScreensaverRequested.store(false, std::memory_order_relaxed);
// Reset auto-off state
@ -60,10 +82,10 @@ void DisplayIdleService::stopScreensaver() {
backlightOff = false;
// Restore backlight if display was dimmed
auto display = getDisplay();
if (display && wasDimmed) {
display->setBacklightDuty(restoreDuty);
if (wasDimmed) {
setBacklightBrightness(restoreDuty);
}
displayDimmed = wasDimmed ? false : displayDimmed;
}
@ -124,11 +146,11 @@ void DisplayIdleService::updateScreensaver() {
}
void DisplayIdleService::tick() {
if (!lvgl::lock(100)) {
if (!lvgl_try_lock(100)) {
return;
}
if (lv_display_get_default() == nullptr) {
lvgl::unlock();
lvgl_unlock();
return;
}
@ -152,10 +174,7 @@ void DisplayIdleService::tick() {
screensaver->stop();
screensaver.reset();
}
auto display = getDisplay();
if (display) {
display->setBacklightDuty(0);
}
setBacklightBrightness(0);
backlightOff = true;
} else {
updateScreensaver();
@ -163,7 +182,7 @@ void DisplayIdleService::tick() {
}
}
lvgl::unlock();
lvgl_unlock();
// Check stop request early for faster response
if (stopScreensaverRequested.load(std::memory_order_acquire)) {
@ -171,23 +190,22 @@ void DisplayIdleService::tick() {
return;
}
auto display = getDisplay();
if (display != nullptr && display->supportsBacklightDuty()) {
if (hasDisplayWithBacklight()) {
if (!cachedDisplaySettings.backlightTimeoutEnabled || cachedDisplaySettings.backlightTimeoutMs == 0) {
if (displayDimmed) {
display->setBacklightDuty(cachedDisplaySettings.backlightDuty);
setBacklightBrightness(cachedDisplaySettings.backlightDuty);
displayDimmed = false;
}
} else {
if (!displayDimmed && inactive_ms >= cachedDisplaySettings.backlightTimeoutMs) {
if (!lvgl::lock(100)) {
if (!lvgl_try_lock(100)) {
return; // Retry on next tick
}
activateScreensaver();
lvgl::unlock();
lvgl_unlock();
// Turn off backlight for "None" screensaver (just black screen)
if (cachedDisplaySettings.screensaverType == settings::display::ScreensaverType::None) {
display->setBacklightDuty(0);
setBacklightBrightness(0);
}
displayDimmed = true;
} else if (displayDimmed && (inactive_ms < kWakeActivityThresholdMs)) {
@ -231,7 +249,7 @@ void DisplayIdleService::onStop(ServiceContext& service) {
}
void DisplayIdleService::startScreensaver() {
if (!lvgl::lock(100)) {
if (!lvgl_try_lock(100)) {
return;
}
@ -240,12 +258,11 @@ void DisplayIdleService::startScreensaver() {
cachedDisplaySettings = settings::display::loadOrGetDefault();
activateScreensaver();
lvgl::unlock();
lvgl_unlock();
// Turn off backlight for "None" screensaver
auto display = getDisplay();
if (display && cachedDisplaySettings.screensaverType == settings::display::ScreensaverType::None) {
display->setBacklightDuty(0);
if (hasDisplayWithBacklight() && cachedDisplaySettings.screensaverType == settings::display::ScreensaverType::None) {
setBacklightBrightness(0);
}
displayDimmed = true;
}

View File

@ -157,8 +157,17 @@ bool GpsService::startReceiving() {
}
for (const auto& configuration: configurations) {
auto device = std::make_shared<GpsDevice>(configuration);
addGpsDevice(device);
::Device* uart_device;
if (device_get_by_name(configuration.uartName, &uart_device) == ERROR_NONE) {
auto gps_device = std::make_shared<GpsDevice>(
uart_device,
configuration.baudRate,
configuration.model
);
addGpsDevice(gps_device);
} else {
LOG_E(TAG, "Failed to find device %s", configuration.uartName);
}
}
// Reset times before starting devices to avoid race with incoming data

View File

@ -1,16 +1,18 @@
#ifdef ESP_PLATFORM
#include <Tactility/CoreDefines.h>
#include <Tactility/hal/keyboard/KeyboardDevice.h>
#include <Tactility/lvgl/LvglSync.h>
#include <display/lv_display.h>
#include <Tactility/Timer.h>
#include <Tactility/service/ServiceContext.h>
#include <Tactility/service/ServiceManifest.h>
#include <Tactility/service/ServiceRegistration.h>
#include <Tactility/settings/KeyboardSettings.h>
#include <Tactility/Timer.h>
#include <tactility/device.h>
#include <tactility/drivers/backlight.h>
#include <tactility/drivers/keyboard.h>
#include <tactility/lvgl_module.h>
namespace tt::service::keyboardidle {
@ -20,11 +22,15 @@ class KeyboardIdleService final : public Service {
bool keyboardDimmed = false;
settings::keyboard::KeyboardSettings cachedKeyboardSettings;
static std::shared_ptr<hal::keyboard::KeyboardDevice> getKeyboard() {
return hal::findFirstDevice<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
}
static Device* getKeyboardBacklight() {
::Device* keyboard;
if (device_get_first_active_by_type(&KEYBOARD_TYPE, &keyboard) == ERROR_NONE) {
::Device* backlight = nullptr;
keyboard_get_backlight(keyboard, &backlight); // disregard result
device_put(keyboard);
return backlight; // WARNING: did not increase refcount
}
// TODO: Remove after all drivers are migrated
return device_find_by_name("keyboard_backlight");
}
@ -41,14 +47,13 @@ class KeyboardIdleService final : public Service {
// Query LVGL inactivity once for both checks
uint32_t inactive_ms = 0;
if (lvgl::lock(100)) {
if (lvgl_try_lock(100)) {
inactive_ms = lv_display_get_inactive_time(nullptr);
lvgl::unlock();
lvgl_unlock();
}
// Handle keyboard backlight
auto keyboard = getKeyboard();
if (keyboard != nullptr && keyboard->isAttached()) {
if (device_has_active_by_type(&KEYBOARD_TYPE)) {
// If timeout disabled, ensure backlight restored if we had dimmed it
if (!cachedKeyboardSettings.backlightTimeoutEnabled || cachedKeyboardSettings.backlightTimeoutMs == 0) {
if (keyboardDimmed) {
@ -89,8 +94,7 @@ public:
timer = nullptr;
}
// Ensure keyboard restored on stop
auto keyboard = getKeyboard();
if (keyboard && keyboardDimmed) {
if (device_has_active_by_type(&KEYBOARD_TYPE) && keyboardDimmed) {
setKeyboardBacklightBrightness(cachedKeyboardSettings.backlightEnabled ? cachedKeyboardSettings.backlightBrightness : 0);
keyboardDimmed = false;
}

View File

@ -1,25 +1,27 @@
#include <Tactility/lvgl/Statusbar.h>
#include <tactility/lvgl_module.h>
#include "tactility/module.h"
#include <Tactility/Mutex.h>
#include <Tactility/Timer.h>
#include <tactility/drivers/power_supply.h>
#include <tactility/filesystem/file_system.h>
#include <Tactility/lvgl/Lvgl.h>
#include <Tactility/bluetooth/Bluetooth.h>
#include <Tactility/lvgl/LvglSync.h>
#include <Tactility/service/ServiceContext.h>
#include <Tactility/service/ServicePaths.h>
#include <Tactility/service/ServiceRegistration.h>
#include <Tactility/bluetooth/Bluetooth.h>
#include <tactility/drivers/bluetooth.h>
#include <tactility/drivers/bluetooth_serial.h>
#include <tactility/drivers/bluetooth_midi.h>
#include <tactility/device.h>
#include <tactility/drivers/usb_host_hid.h>
#include <tactility/drivers/usb_host_midi.h>
#include <tactility/drivers/usb_host_msc.h>
#include <Tactility/service/gps/GpsService.h>
#include <Tactility/service/wifi/Wifi.h>
#include <tactility/check.h>
#include <tactility/device.h>
#include <tactility/drivers/bluetooth.h>
#include <tactility/drivers/bluetooth_midi.h>
#include <tactility/drivers/bluetooth_serial.h>
#include <tactility/drivers/power_supply.h>
#include <tactility/drivers/usb_host_hid.h>
#include <tactility/drivers/usb_host_midi.h>
#include <tactility/drivers/usb_host_msc.h>
#include <tactility/filesystem/file_system.h>
#include <tactility/lvgl_icon_statusbar.h>
@ -267,15 +269,15 @@ class StatusbarService final : public Service {
}
void update() {
if (lvgl::isStarted()) {
if (lvgl::lock(100)) {
if (module_is_started(&lvgl_module)) {
if (lvgl_try_lock(100)) {
updateGpsIcon();
updateBluetoothIcon();
updateWifiIcon();
updateSdCardIcon();
updatePowerStatusIcon();
updateUsbIcon();
lvgl::unlock();
lvgl_unlock();
}
}
}

View File

@ -4,7 +4,6 @@
#include <Tactility/file/PropertiesFile.h>
#include <Tactility/Paths.h>
#include <tactility/hal/Device.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <map>
#include <string>

View File

@ -1,23 +0,0 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool tt_gps_has_coordinates();
bool tt_gps_get_coordinates(
float* longitude,
float* latitude,
float* speed,
float* course,
int* day,
int* month,
int* year
);
#ifdef __cplusplus
}
#endif

View File

@ -1,34 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
DEVICE_TYPE_I2C,
DEVICE_TYPE_DISPLAY,
DEVICE_TYPE_TOUCH,
DEVICE_TYPE_SDCARD,
DEVICE_TYPE_KEYBOARD,
DEVICE_TYPE_POWER,
DEVICE_TYPE_GPS
} TtDeviceType;
typedef uint32_t DeviceId;
/**
* Find one or more devices of a certain type.
* @param[in] type the type to look for
* @param[inout] deviceIds the output ids, which should fit at least maxCount amount of devices
* @param[out] count the resulting number of device ids that were returned
* @param[in] maxCount the maximum number of items that the "deviceIds" output can contain (minimum value is 1)
* @return true if one or more devices were found
*/
bool tt_hal_device_find(TtDeviceType type, DeviceId* deviceIds, uint16_t* count, uint16_t maxCount);
#ifdef __cplusplus
}
#endif

View File

@ -1,100 +0,0 @@
#pragma once
#include <tt_hal_device.h>
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void* DisplayDriverHandle;
typedef enum {
COLOR_FORMAT_MONOCHROME, // 1 bpp
COLOR_FORMAT_BGR565,
COLOR_FORMAT_BGR565_SWAPPED,
COLOR_FORMAT_RGB565,
COLOR_FORMAT_RGB565_SWAPPED,
COLOR_FORMAT_RGB888
} ColorFormat;
/**
* Check if the display driver interface is supported for this device.
* @param[in] displayId the identifier of the display device
* @return true if the driver is supported.
*/
bool tt_hal_display_driver_supported(DeviceId displayId);
/**
* Allocate a driver object for the specified displayId.
* @warning check whether the driver is supported by calling tt_hal_display_driver_supported() first
* @param[in] displayId the identifier of the display device
* @return the driver handle
*/
DisplayDriverHandle tt_hal_display_driver_alloc(DeviceId displayId);
/**
* Free the memory for the display driver.
* @param[in] handle the display driver handle
*/
void tt_hal_display_driver_free(DisplayDriverHandle handle);
/**
* Lock the display device. Call this function before doing any draw calls.
* Certain display devices are on a shared bus (e.g. SPI) so they must run
* mutually exclusive with other devices on the same bus (e.g. SD card)
* @param[in] handle the display driver handle
* @param[in] timeout the maximum amount of ticks to wait for getting a lock
* @return true if the lock was acquired
*/
bool tt_hal_display_driver_lock(DisplayDriverHandle handle, TickType_t timeout);
/**
* Unlock the display device. Must be called exactly once after locking.
* @param[in] handle the display driver handle
*/
void tt_hal_display_driver_unlock(DisplayDriverHandle handle);
/**
* @param[in] handle the display driver handle
* @return the native color format for this display
*/
ColorFormat tt_hal_display_driver_get_colorformat(DisplayDriverHandle handle);
/**
* @param[in] handle the display driver handle
* @return the horizontal resolution of the display
*/
uint16_t tt_hal_display_driver_get_pixel_width(DisplayDriverHandle handle);
/**
* @param[in] handle the display driver handle
* @return the vertical resolution of the display
*/
uint16_t tt_hal_display_driver_get_pixel_height(DisplayDriverHandle handle);
/**
* Draw pixels on the screen. Make sure to call the lock function first and unlock afterwards.
* Many draw calls can be done inbetween a single lock and unlock.
* @param[in] handle the display driver handle
* @param[in] xStart the starting x coordinate for rendering the pixel data
* @param[in] yStart the starting y coordinate for rendering the pixel data
* @param[in] xEnd the last x coordinate for rendering the pixel data (absolute pixel value, not relative to xStart!)
* @param[in] yEnd the last y coordinate for rendering the pixel data (absolute pixel value, not relative to yStart!)
* @param[in] pixelData a buffer of pixels. the data is placed as "RowRowRowRow". The size depends on the ColorFormat
*/
void tt_hal_display_driver_draw_bitmap(DisplayDriverHandle handle, int xStart, int yStart, int xEnd, int yEnd, const void* pixelData);
/**
* Get direct pointers to the display's hardware frame buffer(s), if supported.
* Only available for panels with direct CPU-addressable frame buffers (e.g. MIPI-DSI/DPI).
* @param[in] handle the display driver handle
* @param[out] outBuffers receives up to 2 frame buffer pointers
* @return number of buffers written to outBuffers (0 if unsupported)
*/
uint8_t tt_hal_display_driver_get_frame_buffers(DisplayDriverHandle handle, void* outBuffers[2]);
#ifdef __cplusplus
}
#endif

View File

@ -1,24 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @deprecated NON-FUNCTIONAL - WILL BE REMOVED SOON */
typedef unsigned int GpioPin;
/** @deprecated NON-FUNCTIONAL - WILL BE REMOVED SOON */
#define GPIO_NO_PIN -1
/** @deprecated NON-FUNCTIONAL - WILL BE REMOVED SOON */
bool tt_hal_gpio_get_level(GpioPin pin);
/** @deprecated NON-FUNCTIONAL - WILL BE REMOVED SOON */
int tt_hal_gpio_get_pin_count();
#ifdef __cplusplus
}
#endif

View File

@ -1,48 +0,0 @@
#pragma once
#include "tt_hal_device.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void* TouchDriverHandle;
/**
* Check if the touch driver interface is supported for this device.
* @param[in] touchDeviceId the identifier of the touch device
* @return true if the driver is supported.
*/
bool tt_hal_touch_driver_supported(DeviceId touchDeviceId);
/**
* Allocate a driver object for the specified touchDeviceId.
* @warning check whether the driver is supported by calling tt_hal_touch_driver_supported() first
* @param[in] touchDeviceId the identifier of the touch device
* @return the driver handle
*/
TouchDriverHandle tt_hal_touch_driver_alloc(DeviceId touchDeviceId);
/**
* Free the memory for the touch driver.
* @param[in] handle the touch driver handle
*/
void tt_hal_touch_driver_free(TouchDriverHandle handle);
/**
* Get the coordinates for the currently touched points on the screen.
*
* @param[in] handle the touch driver handle
* @param[in] x array of X coordinates
* @param[in] y array of Y coordinates
* @param[in] strength array of strengths (with the minimum size of maxPointCount) or NULL
* @param[in] pointCount the number of points currently touched on the screen
* @param[in] maxPointCount the maximum number of points that can be touched at once
*
* @return true when touched and coordinates are available
*/
bool tt_hal_touch_driver_get_touched_points(TouchDriverHandle handle, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount);
#ifdef __cplusplus
}
#endif

View File

@ -1,159 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <freertos/FreeRTOS.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* WARNING: THIS API IS NON-FUNCTIONAL AND DEPRECATED.
* IT WILL BE REMOVED IN A FUTURE RELEASE ONCE OFFICIAL APPS ARE MIGRATED.
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
/**
* @file tt_hal_uart.h
* @brief C HAL interface for UART devices used by Tactility C modules.
*
* This header exposes a minimal, C-compatible UART API that mirrors the higher-level
* C++ UART interface (see Tactility/hal/uart).
*
* General notes:
* - Start the UART before I/O using tt_hal_uart_start(); stop it with tt_hal_uart_stop().
*/
typedef void* UartHandle; /**< Opaque handle to an underlying UART instance. */
/**
* @brief Get the number of UART devices available on this platform.
* @return Count of discoverable UARTs (0 if none).
*/
size_t tt_hal_uart_get_count();
/**
* @brief Get the user-friendly name of a UART by index.
* @param index Zero-based UART index in the range [0, tt_hal_uart_get_count()).
* @param[out] name Destination buffer to receive a null-terminated name.
* @param nameSizeLimit Size in bytes of the destination buffer. The name will be
* truncated to fit and always null-terminated if the size
* is greater than 0.
* @return true if a name was written to the buffer; false if the index is out of range
* or on other failure.
*/
bool tt_hal_uart_get_name(size_t index, char* name, size_t nameSizeLimit);
/**
* @brief Allocate an opaque UART handle by index.
*
* Allocation does not start the hardware; call tt_hal_uart_start() to begin I/O.
*
* @param index Zero-based UART index.
* @return A valid UartHandle on success; NULL on failure (e.g., invalid index or already in use).
*/
UartHandle tt_hal_uart_alloc(size_t index);
/**
* @brief Release a previously allocated UART handle and any associated resources.
* @param handle Handle returned by tt_hal_uart_alloc()
*/
void tt_hal_uart_free(UartHandle handle);
/**
* @brief Start the UART so it can perform I/O.
* @param handle A valid UART handle.
* @return true on success; false on failure.
*/
bool tt_hal_uart_start(UartHandle handle);
/**
* @brief Query whether the UART has been started.
* @param handle A valid UART handle.
* @return true if started; false otherwise.
*/
bool tt_hal_uart_is_started(UartHandle handle);
/**
* @brief Stop the UART
* @param handle A valid UART handle.
* @return true on success; false on failure.
*/
bool tt_hal_uart_stop(UartHandle handle);
/**
* @brief Read up to bufferSize bytes into buffer.
*
* This call may block up to timeout ticks waiting for data. It returns the actual
* number of bytes placed into the buffer, which can be less than bufferSize if
* fewer bytes became available before the timeout expired.
*
* @param handle A valid UART handle.
* @param[out] buffer Destination buffer.
* @param bufferSize Capacity of the destination buffer in bytes.
* @param timeout Maximum time to wait in ticks. Use 0 for non-blocking; use TT_MAX_TICKS
* to wait indefinitely.
* @return The number of bytes read (0 on timeout with no data). Never exceeds bufferSize.
*/
size_t tt_hal_uart_read_bytes(UartHandle handle, char* buffer, size_t bufferSize, TickType_t timeout);
/**
* @brief Read a single byte.
*
* @param handle A valid UART handle.
* @param[out] output Where to store the read byte.
* @param timeout Maximum time to wait in ticks. Use 0 for non-blocking; use TT_MAX_TICKS
* to wait indefinitely.
* @return true if a byte was read and stored in output; false on timeout or failure.
*/
bool tt_hal_uart_read_byte(UartHandle handle, char* output, TickType_t timeout);
/**
* @brief Write up to bufferSize bytes from buffer.
*
* This call may block up to timeout ticks waiting for transmit queue space. It returns
* the number of bytes accepted for transmission.
*
* @param handle A valid UART handle.
* @param[in] buffer Source buffer containing bytes to write.
* @param bufferSize Number of bytes to write from buffer.
* @param timeout Maximum time to wait in ticks. Use 0 for non-blocking; use TT_MAX_TICKS
* to wait indefinitely.
* @return The number of bytes written (may be less than bufferSize on timeout).
*/
size_t tt_hal_uart_write_bytes(UartHandle handle, const char* buffer, size_t bufferSize, TickType_t timeout);
/**
* @brief Get the number of bytes currently available to read without blocking.
* @param handle A valid UART handle.
* @return The count of bytes available in the receive buffer.
*/
size_t tt_hal_uart_available(UartHandle handle);
/**
* @brief Set the UART baud rate.
* @param handle A valid UART handle.
* @param baud_rate Desired baud rate in bits per second (e.g., 115200).
* @return true on success; false if the rate is unsupported or on error.
*/
bool tt_hal_uart_set_baud_rate(UartHandle handle, size_t baud_rate);
/**
* @brief Get the current UART baud rate.
* @param handle A valid UART handle.
* @return The configured baud rate in bits per second.
*/
uint32_t tt_hal_uart_get_baud_rate(UartHandle handle);
/**
* @brief Flush the UART input (receive) buffer, discarding any unread data.
* @param handle A valid UART handle.
*/
void tt_hal_uart_flush_input(UartHandle handle);
#ifdef __cplusplus
}
#endif

View File

@ -1,29 +0,0 @@
#pragma once
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#ifdef __cplusplus
extern "C" {
#endif
#define TT_LVGL_DEFAULT_LOCK_TIME 500 // 500 ticks = 500 ms
/** @return true if LVGL is started and active */
bool tt_lvgl_is_started();
/** Start LVGL and related background services */
void tt_lvgl_start();
/** Stop LVGL and related background services */
void tt_lvgl_stop();
/** Lock the LVGL context. Call this before doing LVGL-related operations from a non-LVLG thread */
bool tt_lvgl_lock(TickType_t timeout);
/** Unlock the LVGL context */
void tt_lvgl_unlock();
#ifdef __cplusplus
}
#endif

View File

@ -1,45 +0,0 @@
#include "tt_gps.h"
#include <Tactility/service/gps/GpsService.h>
using namespace tt::service;
extern "C" {
bool tt_gps_has_coordinates() {
auto service = gps::findGpsService();
return service != nullptr && service->hasCoordinates();
}
bool tt_gps_get_coordinates(
float* longitude,
float* latitude,
float* speed,
float* course,
int* day,
int* month,
int* year
) {
auto service = gps::findGpsService();
if (service == nullptr) {
return false;
}
minmea_sentence_rmc rmc;
if (!service->getCoordinates(rmc)) {
return false;
}
*longitude = minmea_tocoord(&rmc.longitude);
*latitude = minmea_tocoord(&rmc.latitude);
*speed = minmea_tocoord(&rmc.speed);
*course = minmea_tocoord(&rmc.course);
*day = rmc.date.day;
*month = rmc.date.month;
*year = rmc.date.year;
return true;
}
}

View File

@ -1,48 +0,0 @@
#include "tt_hal_device.h"
#include <tactility/check.h>
#include <tactility/hal/Device.h>
static tt::hal::Device::Type toTactilityDeviceType(TtDeviceType type) {
switch (type) {
case DEVICE_TYPE_I2C:
return tt::hal::Device::Type::I2c;
case DEVICE_TYPE_DISPLAY:
return tt::hal::Device::Type::Display;
case DEVICE_TYPE_TOUCH:
return tt::hal::Device::Type::Touch;
case DEVICE_TYPE_SDCARD:
return tt::hal::Device::Type::SdCard;
case DEVICE_TYPE_KEYBOARD:
return tt::hal::Device::Type::Keyboard;
case DEVICE_TYPE_POWER:
return tt::hal::Device::Type::Power;
case DEVICE_TYPE_GPS:
return tt::hal::Device::Type::Gps;
default:
check(false, "Device::Type not supported");
}
}
extern "C" {
bool tt_hal_device_find(TtDeviceType type, DeviceId* deviceIds, uint16_t* count, uint16_t maxCount) {
assert(maxCount > 0);
int16_t currentIndex = -1;
uint16_t maxIndex = maxCount - 1;
findDevices<tt::hal::Device>(toTactilityDeviceType(type), [&](const auto& device) {
currentIndex++;
deviceIds[currentIndex] = device->getId();
// Continue if there is storage capacity left
return currentIndex < maxIndex;
});
*count = currentIndex + 1;
return currentIndex >= 0;
}
}

View File

@ -1,93 +0,0 @@
#include "tt_hal_display.h"
#include <tactility/check.h>
#include <tactility/hal/Device.h>
#include <Tactility/hal/display/DisplayDevice.h>
#include <Tactility/hal/display/DisplayDriver.h>
static ColorFormat toColorFormat(tt::hal::display::ColorFormat format) {
switch (format) {
case tt::hal::display::ColorFormat::Monochrome:
return COLOR_FORMAT_MONOCHROME;
case tt::hal::display::ColorFormat::BGR565:
return COLOR_FORMAT_BGR565;
case tt::hal::display::ColorFormat::BGR565Swapped:
return COLOR_FORMAT_BGR565_SWAPPED;
case tt::hal::display::ColorFormat::RGB565:
return COLOR_FORMAT_RGB565;
case tt::hal::display::ColorFormat::RGB565Swapped:
return COLOR_FORMAT_RGB565_SWAPPED;
case tt::hal::display::ColorFormat::RGB888:
return COLOR_FORMAT_RGB888;
default:
check(false, "ColorFormat not supported");
}
}
struct DriverWrapper {
std::shared_ptr<tt::hal::display::DisplayDriver> driver;
DriverWrapper(std::shared_ptr<tt::hal::display::DisplayDriver> driver) : driver(driver) {}
};
static std::shared_ptr<tt::hal::display::DisplayDevice> findValidDisplayDevice(tt::hal::Device::Id id) {
auto device = tt::hal::findDevice(id);
if (device == nullptr || device->getType() != tt::hal::Device::Type::Display) {
return nullptr;
}
return std::reinterpret_pointer_cast<tt::hal::display::DisplayDevice>(device);
}
extern "C" {
bool tt_hal_display_driver_supported(DeviceId id) {
auto display = findValidDisplayDevice(id);
return display != nullptr && display->supportsDisplayDriver();
}
DisplayDriverHandle tt_hal_display_driver_alloc(DeviceId id) {
auto display = findValidDisplayDevice(id);
assert(display->supportsDisplayDriver());
return new DriverWrapper(display->getDisplayDriver());
}
void tt_hal_display_driver_free(DisplayDriverHandle handle) {
auto wrapper = static_cast<DriverWrapper*>(handle);
delete wrapper;
}
bool tt_hal_display_driver_lock(DisplayDriverHandle handle, TickType_t timeout) {
// TODO: re-implement with SPI lock
return true;
}
void tt_hal_display_driver_unlock(DisplayDriverHandle handle) {
// TODO: re-implement with SPI lock
}
ColorFormat tt_hal_display_driver_get_colorformat(DisplayDriverHandle handle) {
auto wrapper = static_cast<DriverWrapper*>(handle);
return toColorFormat(wrapper->driver->getColorFormat());
}
uint16_t tt_hal_display_driver_get_pixel_width(DisplayDriverHandle handle) {
auto wrapper = static_cast<DriverWrapper*>(handle);
return wrapper->driver->getPixelWidth();
}
uint16_t tt_hal_display_driver_get_pixel_height(DisplayDriverHandle handle) {
auto wrapper = static_cast<DriverWrapper*>(handle);
return wrapper->driver->getPixelHeight();
}
void tt_hal_display_driver_draw_bitmap(DisplayDriverHandle handle, int xStart, int yStart, int xEnd, int yEnd, const void* pixelData) {
auto wrapper = static_cast<DriverWrapper*>(handle);
wrapper->driver->drawBitmap(xStart, yStart, xEnd, yEnd, pixelData);
}
uint8_t tt_hal_display_driver_get_frame_buffers(DisplayDriverHandle handle, void* outBuffers[2]) {
auto wrapper = static_cast<DriverWrapper*>(handle);
return wrapper->driver->getFrameBuffers(outBuffers);
}
}

View File

@ -1,43 +0,0 @@
#include "tt_hal_touch.h"
#include <tactility/hal/Device.h>
#include "Tactility/hal/touch/TouchDevice.h"
#include "Tactility/hal/touch/TouchDriver.h"
struct DriverWrapper {
std::shared_ptr<tt::hal::touch::TouchDriver> driver;
DriverWrapper(std::shared_ptr<tt::hal::touch::TouchDriver> driver) : driver(driver) {}
};
static std::shared_ptr<tt::hal::touch::TouchDevice> findValidTouchDevice(tt::hal::Device::Id id) {
auto device = tt::hal::findDevice(id);
if (device == nullptr || device->getType() != tt::hal::Device::Type::Touch) {
return nullptr;
}
return std::reinterpret_pointer_cast<tt::hal::touch::TouchDevice>(device);
}
extern "C" {
bool tt_hal_touch_driver_supported(DeviceId id) {
auto touch = findValidTouchDevice(id);
return touch != nullptr && touch->supportsTouchDriver();
}
TouchDriverHandle tt_hal_touch_driver_alloc(DeviceId id) {
auto touch = findValidTouchDevice(id);
assert(touch->supportsTouchDriver());
return new DriverWrapper(touch->getTouchDriver());
}
void tt_hal_touch_driver_free(TouchDriverHandle handle) {
DriverWrapper* wrapper = static_cast<DriverWrapper*>(handle);
delete wrapper;
}
bool tt_hal_touch_driver_get_touched_points(TouchDriverHandle handle, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) {
DriverWrapper* wrapper = static_cast<DriverWrapper*>(handle);
return wrapper->driver->getTouchedPoints(x, y, strength, pointCount, maxPointCount);
}
}

View File

@ -1,61 +0,0 @@
#include "tt_hal_uart.h"
#include <cstring>
extern "C" {
size_t tt_hal_uart_get_count() {
return 0;
}
bool tt_hal_uart_get_name(size_t index, char* name, size_t nameSizeLimit) {
return false;
}
UartHandle tt_hal_uart_alloc(size_t index) {
return nullptr;
}
void tt_hal_uart_free(UartHandle handle) {
}
bool tt_hal_uart_start(UartHandle handle) {
return false;
}
bool tt_hal_uart_is_started(UartHandle handle) {
return false;
}
bool tt_hal_uart_stop(UartHandle handle) {
return false;
}
size_t tt_hal_uart_read_bytes(UartHandle handle, char* buffer, size_t bufferSize, TickType_t timeout) {
return 0;
}
bool tt_hal_uart_read_byte(UartHandle handle, char* output, TickType_t timeout) {
return false;
}
size_t tt_hal_uart_write_bytes(UartHandle handle, const char* buffer, size_t bufferSize, TickType_t timeout) {
return 0;
}
size_t tt_hal_uart_available(UartHandle handle) {
return 0;
}
bool tt_hal_uart_set_baud_rate(UartHandle handle, size_t baud_rate) {
return false;
}
uint32_t tt_hal_uart_get_baud_rate(UartHandle handle) {
return 0;
}
void tt_hal_uart_flush_input(UartHandle handle) {
}
}

View File

@ -5,13 +5,7 @@
#include "tt_app_fileselection.h"
#include "tt_app_selectiondialog.h"
#include "tt_bundle.h"
#include "tt_gps.h"
#include "tt_hal_device.h"
#include "tt_hal_display.h"
#include "tt_hal_touch.h"
#include "tt_hal_uart.h"
#include <tt_lock.h>
#include "tt_lvgl.h"
#include "tt_lvgl_keyboard.h"
#include "tt_lvgl_spinner.h"
#include "tt_lvgl_toolbar.h"
@ -299,42 +293,6 @@ const esp_elfsym main_symbols[] {
ESP_ELFSYM_EXPORT(tt_bundle_put_bool),
ESP_ELFSYM_EXPORT(tt_bundle_put_int32),
ESP_ELFSYM_EXPORT(tt_bundle_put_string),
ESP_ELFSYM_EXPORT(tt_gps_has_coordinates),
ESP_ELFSYM_EXPORT(tt_gps_get_coordinates),
ESP_ELFSYM_EXPORT(tt_hal_device_find),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_alloc),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_draw_bitmap),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_free),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_get_colorformat),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_get_pixel_height),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_get_pixel_width),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_lock),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_unlock),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_supported),
ESP_ELFSYM_EXPORT(tt_hal_display_driver_get_frame_buffers),
ESP_ELFSYM_EXPORT(tt_hal_touch_driver_supported),
ESP_ELFSYM_EXPORT(tt_hal_touch_driver_alloc),
ESP_ELFSYM_EXPORT(tt_hal_touch_driver_free),
ESP_ELFSYM_EXPORT(tt_hal_touch_driver_get_touched_points),
ESP_ELFSYM_EXPORT(tt_hal_uart_get_count),
ESP_ELFSYM_EXPORT(tt_hal_uart_get_name),
ESP_ELFSYM_EXPORT(tt_hal_uart_alloc),
ESP_ELFSYM_EXPORT(tt_hal_uart_free),
ESP_ELFSYM_EXPORT(tt_hal_uart_start),
ESP_ELFSYM_EXPORT(tt_hal_uart_is_started),
ESP_ELFSYM_EXPORT(tt_hal_uart_stop),
ESP_ELFSYM_EXPORT(tt_hal_uart_read_bytes),
ESP_ELFSYM_EXPORT(tt_hal_uart_read_byte),
ESP_ELFSYM_EXPORT(tt_hal_uart_write_bytes),
ESP_ELFSYM_EXPORT(tt_hal_uart_available),
ESP_ELFSYM_EXPORT(tt_hal_uart_set_baud_rate),
ESP_ELFSYM_EXPORT(tt_hal_uart_get_baud_rate),
ESP_ELFSYM_EXPORT(tt_hal_uart_flush_input),
ESP_ELFSYM_EXPORT(tt_lvgl_is_started),
ESP_ELFSYM_EXPORT(tt_lvgl_lock),
ESP_ELFSYM_EXPORT(tt_lvgl_unlock),
ESP_ELFSYM_EXPORT(tt_lvgl_start),
ESP_ELFSYM_EXPORT(tt_lvgl_stop),
ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_show),
ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_hide),
ESP_ELFSYM_EXPORT(tt_lvgl_software_keyboard_is_enabled),

View File

@ -1,26 +0,0 @@
#include <Tactility/lvgl/Lvgl.h>
#include <Tactility/lvgl/LvglSync.h>
extern "C" {
bool tt_lvgl_is_started() {
return tt::lvgl::isStarted();
}
void tt_lvgl_start() {
tt::lvgl::start();
}
void tt_lvgl_stop() {
tt::lvgl::stop();
}
bool tt_lvgl_lock(TickType_t timeout) {
return tt::lvgl::getSyncLock()->lock(timeout);
}
void tt_lvgl_unlock() {
tt::lvgl::getSyncLock()->unlock();
}
}

View File

@ -392,6 +392,15 @@ error_t device_get_first_by_type(const struct DeviceType* type, struct Device**
*/
error_t device_get_first_active_by_type(const struct DeviceType* type, struct Device** out_device);
/**
* Check if there is an active device of the provided type.
*
* @param[in] type non-null device type pointer
* @retval ERROR_NOT_FOUND if no started device of that type exists
* @retval ERROR_NONE on success; caller must call device_put(*out_device) exactly once
*/
bool device_has_active_by_type(const struct DeviceType* type);
/**
* Find the first device whose driver matches the given compatible string and atomically take a
* reference on it. See device_get_by_name() for why this is preferred over

View File

@ -37,6 +37,15 @@ struct KeyboardApi {
* @retval ERROR_NONE when the operation was successful
*/
error_t (*read_key)(struct Device* device, struct KeyboardKeyData* data);
/**
* @brief Returns the baclight if the keyboard has one.
* @param[in] device the keyboard device
* @param[out] backlight_device the output backlight device
* @retval ERROR_NONE when the backlight_device was set
* @retval ERROR_NOT_SUPPORTED when this device has no backlight
*/
error_t (*get_backlight)(struct Device* device, struct Device** backlight_device);
};
/**
@ -44,6 +53,16 @@ struct KeyboardApi {
*/
error_t keyboard_read_key(struct Device* device, struct KeyboardKeyData* data);
/**
* @brief Returns the backlight if the keyboard has one.
* @param[in] device the keyboard device
* @param[out] backlight_device the output backlight device
* @retval ERROR_NONE when the backlight_device was set
* @retval ERROR_NOT_SUPPORTED when this device has no backlight
*/
error_t keyboard_get_backlight(struct Device* device, struct Device** backlight_device);
extern const struct DeviceType KEYBOARD_TYPE;
#ifdef __cplusplus

View File

@ -585,6 +585,20 @@ error_t device_get_first_active_by_type(const DeviceType* type, Device** out_dev
return error;
}
bool device_has_active_by_type(const struct DeviceType* type) {
ledger_lock();
bool found = false;
for (auto* device : ledger.devices) {
auto* driver = device->internal->driver;
if (driver != nullptr && driver->device_type == type && device->internal->state.started) {
found = true;
break;
}
}
ledger_unlock();
return found;
}
error_t device_get_first_by_compatible(const char* compatible, Device** out_device) {
ledger_lock();
Device* found = nullptr;

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/drivers/keyboard.h>
#include <tactility/error.h>
#include <tactility/device.h>
#define KEYBOARD_DRIVER_API(driver) ((struct KeyboardApi*)driver->api)
@ -11,6 +12,16 @@ error_t keyboard_read_key(Device* device, KeyboardKeyData* data) {
return KEYBOARD_DRIVER_API(driver)->read_key(device, data);
}
error_t keyboard_get_backlight(Device* device, Device** backlight_device) {
const auto* driver = device_get_driver(device);
if (KEYBOARD_DRIVER_API(driver)->get_backlight == nullptr) {
return ERROR_NOT_SUPPORTED;
}
return KEYBOARD_DRIVER_API(driver)->get_backlight(device, backlight_device);
}
const DeviceType KEYBOARD_TYPE {
.name = "keyboard"
};