mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-08-20 17:05:06 +00:00
Device implementations and touch calibration updates
This commit is contained in:
parent
fa4a6e255c
commit
8f5e92d6e4
@ -1,7 +1,6 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
file(GLOB_RECURSE SOURCE_FILES source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port ILI934x XPT2046 PwmBacklight driver vfs fatfs
|
||||
REQUIRES TactilityKernel driver
|
||||
)
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
#include "devices/Display.h"
|
||||
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <PwmBacklight.h>
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
static bool initBoot() {
|
||||
return driver::pwmbacklight::init(LCD_PIN_BACKLIGHT);
|
||||
}
|
||||
|
||||
static DeviceVector createDevices() {
|
||||
return {
|
||||
createDisplay()
|
||||
};
|
||||
}
|
||||
|
||||
extern const Configuration hardwareConfiguration = {
|
||||
.initBoot = initBoot,
|
||||
.createDevices = createDevices
|
||||
};
|
||||
@ -1,46 +0,0 @@
|
||||
#include "Display.h"
|
||||
#include "Xpt2046Touch.h"
|
||||
#include <Ili934xDisplay.h>
|
||||
#include <PwmBacklight.h>
|
||||
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch(esp_lcd_spi_bus_handle_t spiDevice) {
|
||||
auto configuration = std::make_unique<Xpt2046Touch::Configuration>(
|
||||
spiDevice,
|
||||
TOUCH_CS_PIN,
|
||||
LCD_HORIZONTAL_RESOLUTION,
|
||||
LCD_VERTICAL_RESOLUTION,
|
||||
true, // swapXY
|
||||
false, // mirrorX
|
||||
true // mirrorY
|
||||
);
|
||||
return std::make_shared<Xpt2046Touch>(std::move(configuration));
|
||||
}
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
auto spi_configuration = std::make_shared<Ili934xDisplay::SpiConfiguration>(Ili934xDisplay::SpiConfiguration {
|
||||
.spiHostDevice = LCD_SPI_HOST,
|
||||
.csPin = LCD_PIN_CS,
|
||||
.dcPin = LCD_PIN_DC,
|
||||
.pixelClockFrequency = 40'000'000,
|
||||
.transactionQueueDepth = 10
|
||||
});
|
||||
|
||||
Ili934xDisplay::Configuration panel_configuration = {
|
||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
||||
.gapX = 0,
|
||||
.gapY = 0,
|
||||
.swapXY = true,
|
||||
.mirrorX = true,
|
||||
.mirrorY = true,
|
||||
.invertColor = false,
|
||||
.swapBytes = true,
|
||||
.bufferSize = LCD_BUFFER_SIZE,
|
||||
.touch = createTouch(spi_configuration->spiHostDevice),
|
||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
||||
.resetPin = LCD_PIN_RST,
|
||||
.rgbElementOrder = LCD_RGB_ELEMENT_ORDER_RGB
|
||||
};
|
||||
|
||||
return std::make_shared<Ili934xDisplay>(panel_configuration, spi_configuration, true);
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <memory>
|
||||
|
||||
// Display
|
||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
||||
constexpr auto LCD_PIN_CS = GPIO_NUM_15;
|
||||
constexpr auto LCD_PIN_DC = GPIO_NUM_2;
|
||||
constexpr auto LCD_PIN_RST = GPIO_NUM_NC; // tied to ESP32 RST
|
||||
constexpr auto LCD_PIN_CLK = GPIO_NUM_14;
|
||||
constexpr auto LCD_PIN_MOSI = GPIO_NUM_13;
|
||||
constexpr auto LCD_PIN_MISO = GPIO_NUM_12;
|
||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 240;
|
||||
constexpr auto LCD_VERTICAL_RESOLUTION = 320;
|
||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 10;
|
||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
||||
|
||||
// Backlight
|
||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_27;
|
||||
|
||||
// Touch
|
||||
constexpr auto TOUCH_CS_PIN = GPIO_NUM_33;
|
||||
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_36;
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
||||
@ -5,9 +5,10 @@
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_sdspi.h>
|
||||
#include <tactility/bindings/display_placeholder.h>
|
||||
#include <tactility/bindings/pointer_placeholder.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <tactility/bindings/esp32_ledc_backlight.h>
|
||||
#include <bindings/ili9341.h>
|
||||
#include <bindings/xpt2046.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
@ -23,6 +24,15 @@
|
||||
gpio-count = <40>;
|
||||
};
|
||||
|
||||
display_backlight {
|
||||
compatible = "espressif,esp32-ledc-backlight";
|
||||
// Off by default so display power-on won't show the screen from before the last power loss.
|
||||
// The display backlight is turned on during the boot process.
|
||||
status = "disabled";
|
||||
pin-backlight = <&gpio0 27 GPIO_FLAG_NONE>;
|
||||
frequency-hz = <512>;
|
||||
};
|
||||
|
||||
spi0 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
@ -33,11 +43,23 @@
|
||||
<&gpio0 33 GPIO_FLAG_NONE>; // Touch
|
||||
|
||||
display@0 {
|
||||
compatible = "display-placeholder";
|
||||
compatible = "ilitek,ili9341";
|
||||
horizontal-resolution = <240>;
|
||||
vertical-resolution = <320>;
|
||||
swap-xy;
|
||||
mirror-x;
|
||||
mirror-y;
|
||||
pixel-clock-hz = <40000000>;
|
||||
pin-dc = <&gpio0 2 GPIO_FLAG_NONE>;
|
||||
backlight = <&display_backlight>;
|
||||
};
|
||||
|
||||
touch@1 {
|
||||
compatible = "pointer-placeholder";
|
||||
compatible = "xptek,xpt2046";
|
||||
x-max = <240>;
|
||||
y-max = <320>;
|
||||
swap-xy;
|
||||
mirror-y;
|
||||
};
|
||||
};
|
||||
|
||||
@ -61,4 +83,4 @@
|
||||
pin-tx = <&gpio0 1 GPIO_FLAG_NONE>;
|
||||
pin-rx = <&gpio0 3 GPIO_FLAG_NONE>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -7,6 +7,8 @@ hardware.target=ESP32
|
||||
hardware.flashSize=4MB
|
||||
hardware.spiRam=false
|
||||
|
||||
dependencies.useDeprecatedHal=false
|
||||
|
||||
storage.userDataLocation=SD
|
||||
|
||||
display.size=2.4"
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/ili9341-module
|
||||
- Drivers/xpt2046-module
|
||||
dts: cyd,2432s024r.dts
|
||||
|
||||
@ -13,6 +13,9 @@ display.size=2.8"
|
||||
display.shape=rectangle
|
||||
display.dpi=143
|
||||
|
||||
touch.calibrationSupported=true
|
||||
touch.calibrationRequired=false
|
||||
|
||||
cdn.warningMessage=There are 3 hardware variants of this board. This build works on the original variant only ("v1").
|
||||
|
||||
lvgl.colorDepth=16
|
||||
|
||||
@ -16,6 +16,9 @@ display.size=3.5"
|
||||
display.shape=rectangle
|
||||
display.dpi=165
|
||||
|
||||
touch.calibrationSupported=true
|
||||
touch.calibrationRequired=true
|
||||
|
||||
cdn.warningMessage=Put the device into bootloader mode by pressing the center nav button and reset for 2-3 seconds, then release reset, then release the nav button.<br/>After flashing is finished, press the reset button to reboot.
|
||||
|
||||
lvgl.colorDepth=24
|
||||
|
||||
@ -1,30 +1,10 @@
|
||||
#include "Xpt2046Touch.h"
|
||||
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <esp_err.h>
|
||||
#include <esp_lcd_touch_xpt2046.h>
|
||||
|
||||
static void processCoordinates(esp_lcd_touch_handle_t tp, uint16_t* x, uint16_t* y, uint16_t* strength, uint8_t* pointCount, uint8_t maxPointCount) {
|
||||
(void)strength;
|
||||
if (tp == nullptr || x == nullptr || y == nullptr || pointCount == nullptr || *pointCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* config = static_cast<Xpt2046Touch::Configuration*>(tp->config.user_data);
|
||||
if (config == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto settings = tt::settings::touch::getActive();
|
||||
const auto points = std::min<uint8_t>(*pointCount, maxPointCount);
|
||||
for (uint8_t i = 0; i < points; i++) {
|
||||
tt::settings::touch::applyCalibration(settings, config->xMax, config->yMax, x[i], y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool Xpt2046Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) {
|
||||
const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(configuration->spiPinCs);
|
||||
return esp_lcd_new_panel_io_spi(configuration->spiDevice, &io_config, &outHandle) == ESP_OK;
|
||||
@ -49,7 +29,7 @@ esp_lcd_touch_config_t Xpt2046Touch::createEspLcdTouchConfig() {
|
||||
.mirror_x = configuration->mirrorX,
|
||||
.mirror_y = configuration->mirrorY,
|
||||
},
|
||||
.process_coordinates = processCoordinates,
|
||||
.process_coordinates = nullptr,
|
||||
.interrupt_callback = nullptr,
|
||||
.user_data = configuration.get(),
|
||||
.driver_data = nullptr
|
||||
|
||||
@ -56,6 +56,4 @@ public:
|
||||
std::string getName() const final { return "XPT2046"; }
|
||||
|
||||
std::string getDescription() const final { return "XPT2046 SPI touch driver"; }
|
||||
|
||||
bool supportsCalibration() const override { return true; }
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "Xpt2046SoftSpi.h"
|
||||
|
||||
#include <tactility/log.h>
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -198,9 +197,6 @@ bool Xpt2046SoftSpi::getTouchPoint(Point& point) {
|
||||
uint16_t x = static_cast<uint16_t>(std::clamp(mappedX, 0, static_cast<int>(configuration->xMax)));
|
||||
uint16_t y = static_cast<uint16_t>(std::clamp(mappedY, 0, static_cast<int>(configuration->yMax)));
|
||||
|
||||
const auto calibration = tt::settings::touch::getActive();
|
||||
tt::settings::touch::applyCalibration(calibration, configuration->xMax, configuration->yMax, x, y);
|
||||
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
return true;
|
||||
|
||||
@ -99,7 +99,6 @@ public:
|
||||
bool stopLvgl() override;
|
||||
|
||||
bool supportsTouchDriver() override { return true; }
|
||||
bool supportsCalibration() const override { return true; }
|
||||
std::shared_ptr<tt::hal::touch::TouchDriver> getTouchDriver() override;
|
||||
lv_indev_t* getLvglIndev() override { return lvglDevice; }
|
||||
|
||||
|
||||
@ -102,4 +102,11 @@ menu "Tactility App"
|
||||
help
|
||||
The minimum time to show the splash screen in milliseconds.
|
||||
When set to 0, startup will continue to desktop as soon as boot operations are finished.
|
||||
config TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
bool "Set true when a touch screen calibration app should be included"
|
||||
default n
|
||||
config TT_TOUCH_CALIBRATION_REQUIRED
|
||||
bool "Set true when a touch screen calibration is required before the device is usable"
|
||||
default n
|
||||
depends on TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
endmenu
|
||||
|
||||
@ -10,6 +10,56 @@ extern "C" {
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
/**
|
||||
* @brief Linear per-axis calibration range for raw pointer coordinates.
|
||||
*
|
||||
* Values are the raw (pre-calibration) coordinates that should map to the display's
|
||||
* [0, hor_res-1] / [0, ver_res-1] range. Corrects scale+offset error only; axis
|
||||
* swap/mirror is handled separately by PointerApi and applied by the driver before
|
||||
* lvgl_pointer_read_cb() sees the coordinates.
|
||||
*/
|
||||
struct LvglPointerCalibration {
|
||||
int32_t x_min;
|
||||
int32_t x_max;
|
||||
int32_t y_min;
|
||||
int32_t y_max;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sets (or clears, when calibration is NULL) the calibration applied to raw coordinates
|
||||
* read from the device before they are written into LVGL indev data, on an indev previously
|
||||
* created with lvgl_pointer_add().
|
||||
*
|
||||
* @warning Caller must hold the LVGL lock (see lvgl_lock() in lvgl_module.h).
|
||||
*
|
||||
* @param[in] indev an indev previously created by lvgl_pointer_add()
|
||||
* @param[in] calibration the calibration range to apply, or NULL to clear/disable calibration
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_INVALID_ARGUMENT if indev is NULL, or calibration is non-NULL but invalid
|
||||
* (x_max <= x_min, y_max <= y_min, or either span smaller than the minimum allowed range)
|
||||
*/
|
||||
error_t lvgl_pointer_set_calibration(lv_indev_t* indev, const struct LvglPointerCalibration* calibration);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the calibration currently active on indev, if any.
|
||||
* @warning Caller must hold the LVGL lock.
|
||||
* @return true when a calibration is currently set on indev (out_calibration is filled), false otherwise
|
||||
*/
|
||||
bool lvgl_pointer_get_calibration(lv_indev_t* indev, struct LvglPointerCalibration* out_calibration);
|
||||
|
||||
/**
|
||||
* @brief Returns the first indev created by lvgl_pointer_add() that hasn't been removed yet.
|
||||
*
|
||||
* Unlike iterating LVGL's own indev list, this only ever returns an indev created by
|
||||
* lvgl_pointer_add() — safe to pass to lvgl_pointer_set_calibration()/lvgl_pointer_get_calibration()
|
||||
* without risking a foreign indev (e.g. one registered by the deprecated HAL layer) whose driver
|
||||
* data isn't a struct LvglPointerCtx*.
|
||||
*
|
||||
* @warning Caller must hold the LVGL lock.
|
||||
* @return the indev, or NULL if none is currently registered.
|
||||
*/
|
||||
lv_indev_t* lvgl_pointer_get_default(void);
|
||||
|
||||
/**
|
||||
* @brief Creates an lv_indev_t bound to the given POINTER_TYPE device and registers a read callback
|
||||
* that polls the device through its PointerApi.
|
||||
|
||||
@ -7,11 +7,52 @@
|
||||
|
||||
struct LvglPointerCtx {
|
||||
struct Device* device;
|
||||
bool calibration_enabled;
|
||||
struct LvglPointerCalibration calibration;
|
||||
};
|
||||
|
||||
// Bus reads are expected to complete quickly; bound the wait so a stalled controller can't block the LVGL indev poll.
|
||||
static const TickType_t LVGL_POINTER_READ_TIMEOUT = pdMS_TO_TICKS(10);
|
||||
|
||||
// Tracks the first indev created by lvgl_pointer_add() still alive, for lvgl_pointer_get_default().
|
||||
// Only ever set/cleared by lvgl_pointer_add()/lvgl_pointer_remove(), so it can never point at an
|
||||
// indev created by other code (e.g. the deprecated HAL's own LVGL pointer registration).
|
||||
static lv_indev_t* default_pointer_indev = NULL;
|
||||
|
||||
// Mirrors Tactility/Source/settings/TouchCalibrationSettings.cpp's isValid().
|
||||
static const int32_t LVGL_POINTER_CALIBRATION_MIN_RANGE = 20;
|
||||
|
||||
static bool lvgl_pointer_calibration_is_valid(const struct LvglPointerCalibration* calibration) {
|
||||
return calibration->x_max > calibration->x_min &&
|
||||
calibration->y_max > calibration->y_min &&
|
||||
(calibration->x_max - calibration->x_min) >= LVGL_POINTER_CALIBRATION_MIN_RANGE &&
|
||||
(calibration->y_max - calibration->y_min) >= LVGL_POINTER_CALIBRATION_MIN_RANGE;
|
||||
}
|
||||
|
||||
// Linear per-axis rescale of [x_min,x_max]/[y_min,y_max] onto [0,target_x_max]/[0,target_y_max],
|
||||
// clamped. Mirrors TouchCalibrationSettings.cpp's applyCalibration(). Kept as a standalone
|
||||
// function (not inlined into the read callback) so the math is isolated and easy to reason about.
|
||||
static void lvgl_pointer_calibration_apply(
|
||||
const struct LvglPointerCalibration* calibration,
|
||||
int32_t target_x_max,
|
||||
int32_t target_y_max,
|
||||
uint16_t* x,
|
||||
uint16_t* y
|
||||
) {
|
||||
int64_t mapped_x = ((int64_t)*x - calibration->x_min) * target_x_max /
|
||||
((int64_t)calibration->x_max - calibration->x_min);
|
||||
int64_t mapped_y = ((int64_t)*y - calibration->y_min) * target_y_max /
|
||||
((int64_t)calibration->y_max - calibration->y_min);
|
||||
|
||||
if (mapped_x < 0) mapped_x = 0;
|
||||
if (mapped_x > target_x_max) mapped_x = target_x_max;
|
||||
if (mapped_y < 0) mapped_y = 0;
|
||||
if (mapped_y > target_y_max) mapped_y = target_y_max;
|
||||
|
||||
*x = (uint16_t)mapped_x;
|
||||
*y = (uint16_t)mapped_y;
|
||||
}
|
||||
|
||||
static void lvgl_pointer_read_cb(lv_indev_t* indev, lv_indev_data_t* data) {
|
||||
struct LvglPointerCtx* ctx = (struct LvglPointerCtx*)lv_indev_get_driver_data(indev);
|
||||
|
||||
@ -26,6 +67,16 @@ static void lvgl_pointer_read_cb(lv_indev_t* indev, lv_indev_data_t* data) {
|
||||
bool touched = pointer_get_touched_points(ctx->device, &x, &y, NULL, &point_count, 1);
|
||||
|
||||
if (touched && point_count > 0) {
|
||||
if (ctx->calibration_enabled) {
|
||||
lv_display_t* display = lv_indev_get_display(indev);
|
||||
if (display != NULL) {
|
||||
int32_t target_x_max = lv_display_get_horizontal_resolution(display) - 1;
|
||||
int32_t target_y_max = lv_display_get_vertical_resolution(display) - 1;
|
||||
if (target_x_max > 0 && target_y_max > 0) {
|
||||
lvgl_pointer_calibration_apply(&ctx->calibration, target_x_max, target_y_max, &x, &y);
|
||||
}
|
||||
}
|
||||
}
|
||||
data->point.x = x;
|
||||
data->point.y = y;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
@ -42,7 +93,7 @@ error_t lvgl_pointer_add(struct Device* device, lv_display_t* display, lv_indev_
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
struct LvglPointerCtx* ctx = (struct LvglPointerCtx*)malloc(sizeof(struct LvglPointerCtx));
|
||||
struct LvglPointerCtx* ctx = (struct LvglPointerCtx*)calloc(1, sizeof(struct LvglPointerCtx));
|
||||
if (ctx == NULL) {
|
||||
return ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -61,16 +112,58 @@ error_t lvgl_pointer_add(struct Device* device, lv_display_t* display, lv_indev_
|
||||
lv_indev_set_display(indev, display);
|
||||
}
|
||||
|
||||
if (default_pointer_indev == NULL) {
|
||||
default_pointer_indev = indev;
|
||||
}
|
||||
|
||||
*out_indev = indev;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
lv_indev_t* lvgl_pointer_get_default(void) {
|
||||
return default_pointer_indev;
|
||||
}
|
||||
|
||||
error_t lvgl_pointer_set_calibration(lv_indev_t* indev, const struct LvglPointerCalibration* calibration) {
|
||||
if (indev == NULL) {
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
struct LvglPointerCtx* ctx = lv_indev_get_driver_data(indev);
|
||||
|
||||
if (calibration == NULL) {
|
||||
ctx->calibration_enabled = false;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
if (!lvgl_pointer_calibration_is_valid(calibration)) {
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
ctx->calibration = *calibration;
|
||||
ctx->calibration_enabled = true;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
bool lvgl_pointer_get_calibration(lv_indev_t* indev, struct LvglPointerCalibration* out_calibration) {
|
||||
if (indev == NULL || out_calibration == NULL) {
|
||||
return false;
|
||||
}
|
||||
struct LvglPointerCtx* ctx = (struct LvglPointerCtx*)lv_indev_get_driver_data(indev);
|
||||
if (!ctx->calibration_enabled) {
|
||||
return false;
|
||||
}
|
||||
*out_calibration = ctx->calibration;
|
||||
return true;
|
||||
}
|
||||
|
||||
void lvgl_pointer_remove(lv_indev_t* indev) {
|
||||
if (indev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct LvglPointerCtx* ctx = (struct LvglPointerCtx*)lv_indev_get_driver_data(indev);
|
||||
if (default_pointer_indev == indev) {
|
||||
default_pointer_indev = NULL;
|
||||
}
|
||||
lv_indev_delete(indev);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#if defined(CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED)
|
||||
|
||||
#include <Tactility/app/App.h>
|
||||
|
||||
namespace tt::app::touchcalibration {
|
||||
@ -7,3 +11,5 @@ namespace tt::app::touchcalibration {
|
||||
LaunchId start();
|
||||
|
||||
} // namespace tt::app::touchcalibration
|
||||
|
||||
#endif
|
||||
|
||||
@ -4,6 +4,14 @@
|
||||
|
||||
namespace tt::settings::touch {
|
||||
|
||||
/**
|
||||
* @brief Persisted touch calibration coefficients.
|
||||
*
|
||||
* Shape mirrors struct LvglPointerCalibration (tactility/lvgl_pointer.h): xMin/xMax/yMin/yMax are
|
||||
* the raw touch coordinate range that should map onto the display's full resolution. This struct
|
||||
* only concerns itself with persistence - applying it to a live pointer indev is the caller's
|
||||
* responsibility (see lvgl_pointer_set_calibration()).
|
||||
*/
|
||||
struct TouchCalibrationSettings {
|
||||
bool enabled = false;
|
||||
int32_t xMin = 0;
|
||||
@ -14,20 +22,12 @@ struct TouchCalibrationSettings {
|
||||
|
||||
TouchCalibrationSettings getDefault();
|
||||
|
||||
bool isValid(const TouchCalibrationSettings& settings);
|
||||
|
||||
bool load(TouchCalibrationSettings& settings);
|
||||
|
||||
TouchCalibrationSettings loadOrGetDefault();
|
||||
|
||||
bool save(const TouchCalibrationSettings& settings);
|
||||
|
||||
bool isValid(const TouchCalibrationSettings& settings);
|
||||
|
||||
TouchCalibrationSettings getActive();
|
||||
|
||||
void setRuntimeCalibrationEnabled(bool enabled);
|
||||
|
||||
void invalidateCache();
|
||||
|
||||
bool applyCalibration(const TouchCalibrationSettings& settings, uint16_t xMax, uint16_t yMax, uint16_t& x, uint16_t& y);
|
||||
|
||||
} // namespace tt::settings::touch
|
||||
|
||||
@ -132,7 +132,9 @@ namespace app {
|
||||
namespace setup { extern const AppManifest manifest; }
|
||||
namespace systeminfo { extern const AppManifest manifest; }
|
||||
namespace timedatesettings { extern const AppManifest manifest; }
|
||||
#ifdef CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
namespace touchcalibration { extern const AppManifest manifest; }
|
||||
#endif
|
||||
namespace timezone { extern const AppManifest manifest; }
|
||||
namespace usbsettings { extern const AppManifest manifest; }
|
||||
namespace btmanage { extern const AppManifest manifest; }
|
||||
@ -193,7 +195,9 @@ static void registerInternalApps() {
|
||||
addAppManifest(app::setup::manifest);
|
||||
addAppManifest(app::systeminfo::manifest);
|
||||
addAppManifest(app::timedatesettings::manifest);
|
||||
#ifdef CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED
|
||||
addAppManifest(app::touchcalibration::manifest);
|
||||
#endif
|
||||
addAppManifest(app::timezone::manifest);
|
||||
addAppManifest(app::wifiapsettings::manifest);
|
||||
addAppManifest(app::wificonnect::manifest);
|
||||
|
||||
@ -11,10 +11,12 @@
|
||||
#include <Tactility/service/displayidle/DisplayIdleService.h>
|
||||
#endif
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/app/touchcalibration/TouchCalibration.h>
|
||||
#include <Tactility/lvgl/Toolbar.h>
|
||||
#include <Tactility/settings/DisplaySettings.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <tactility/lvgl_pointer.h>
|
||||
|
||||
namespace tt::app::kerneldisplay {
|
||||
|
||||
@ -98,6 +100,10 @@ class KernelDisplayApp final : public App {
|
||||
}
|
||||
}
|
||||
|
||||
static void onCalibrateTouchClicked(lv_event_t*) {
|
||||
app::touchcalibration::start();
|
||||
}
|
||||
|
||||
static void onScreensaverChanged(lv_event_t* event) {
|
||||
auto* app = static_cast<KernelDisplayApp*>(lv_event_get_user_data(event));
|
||||
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
||||
@ -251,8 +257,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Note: no touch calibration section here - unlike HalDisplayApp, the kernel PointerApi has
|
||||
// no calibration support yet.
|
||||
if (lvgl_pointer_get_default() != nullptr) {
|
||||
auto* calibrate_wrapper = lv_obj_create(main_wrapper);
|
||||
lv_obj_set_size(calibrate_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(calibrate_wrapper, 0, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(calibrate_wrapper, 0, LV_STATE_DEFAULT);
|
||||
|
||||
auto* calibrate_label = lv_label_create(calibrate_wrapper);
|
||||
lv_label_set_text(calibrate_label, "Touch calibration");
|
||||
lv_obj_align(calibrate_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
|
||||
auto* calibrate_button = lv_button_create(calibrate_wrapper);
|
||||
lv_obj_align(calibrate_button, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_add_event_cb(calibrate_button, onCalibrateTouchClicked, LV_EVENT_SHORT_CLICKED, this);
|
||||
|
||||
auto* calibrate_button_label = lv_label_create(calibrate_button);
|
||||
lv_label_set_text(calibrate_button_label, "Calibrate");
|
||||
lv_obj_center(calibrate_button_label);
|
||||
}
|
||||
}
|
||||
|
||||
void onHide(AppContext& app) override {
|
||||
|
||||
@ -17,6 +17,12 @@
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#if defined(CONFIG_TT_TOUCH_CALIBRATION_REQUIRED)
|
||||
#include <Tactility/app/touchcalibration/TouchCalibration.h>
|
||||
#endif
|
||||
|
||||
namespace tt::app::setup {
|
||||
|
||||
extern const AppManifest manifest;
|
||||
@ -139,6 +145,13 @@ public:
|
||||
|
||||
void onCreate(AppContext& app) override {
|
||||
steps = {
|
||||
#if defined(CONFIG_TT_TOUCH_CALIBRATION_REQUIRED)
|
||||
{
|
||||
.title = "Touch Calibration",
|
||||
.description = "Let's calibrate the touch screen.",
|
||||
.run = [] { touchcalibration::start(); }
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.title = "Time Zone Setup",
|
||||
.description = "Let's set the time zone.",
|
||||
|
||||
@ -166,16 +166,16 @@ class TimeZoneApp final : public App {
|
||||
}
|
||||
|
||||
void updateList() {
|
||||
if (lvgl::lock(100 / portTICK_PERIOD_MS)) {
|
||||
if (lvgl::lock(200 / portTICK_PERIOD_MS)) {
|
||||
std::string filter = string::lowercase(std::string(lv_textarea_get_text(filterTextareaWidget)));
|
||||
readTimeZones(filter);
|
||||
lvgl::unlock();
|
||||
readTimeZones(filter);
|
||||
} else {
|
||||
LOG_E(LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "LVGL");
|
||||
LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "TimeZone LVGL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (lvgl::lock(100 / portTICK_PERIOD_MS)) {
|
||||
if (lvgl::lock(200 / portTICK_PERIOD_MS)) {
|
||||
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
|
||||
lv_obj_clean(listWidget);
|
||||
|
||||
@ -189,6 +189,8 @@ class TimeZoneApp final : public App {
|
||||
}
|
||||
|
||||
lvgl::unlock();
|
||||
} else {
|
||||
LOG_E(TAG, LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, "TimeZone LVGL");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
#include <Tactility/Tactility.h>
|
||||
|
||||
#include <Tactility/app/touchcalibration/TouchCalibration.h>
|
||||
|
||||
#if defined(CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED)
|
||||
|
||||
#include <Tactility/Tactility.h>
|
||||
#include <Tactility/settings/TouchCalibrationSettings.h>
|
||||
|
||||
#include <tactility/log.h>
|
||||
#include <tactility/lvgl_module.h>
|
||||
#include <tactility/lvgl_pointer.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <lvgl.h>
|
||||
@ -30,6 +33,7 @@ class TouchCalibrationApp final : public App {
|
||||
|
||||
Sample samples[4] = {};
|
||||
uint8_t sampleCount = 0;
|
||||
bool calibrationApplied = false;
|
||||
|
||||
lv_obj_t* root = nullptr;
|
||||
lv_obj_t* target = nullptr;
|
||||
@ -73,12 +77,33 @@ class TouchCalibrationApp final : public App {
|
||||
}
|
||||
|
||||
void finishCalibration() {
|
||||
constexpr int32_t MIN_RANGE = 20;
|
||||
const int32_t xLow = (static_cast<int32_t>(samples[0].x) + static_cast<int32_t>(samples[3].x)) / 2;
|
||||
const int32_t xHigh = (static_cast<int32_t>(samples[1].x) + static_cast<int32_t>(samples[2].x)) / 2;
|
||||
const int32_t yLow = (static_cast<int32_t>(samples[0].y) + static_cast<int32_t>(samples[1].y)) / 2;
|
||||
const int32_t yHigh = (static_cast<int32_t>(samples[2].y) + static_cast<int32_t>(samples[3].y)) / 2;
|
||||
|
||||
const int32_t xMin = (static_cast<int32_t>(samples[0].x) + static_cast<int32_t>(samples[3].x)) / 2;
|
||||
const int32_t xMax = (static_cast<int32_t>(samples[1].x) + static_cast<int32_t>(samples[2].x)) / 2;
|
||||
const int32_t yMin = (static_cast<int32_t>(samples[0].y) + static_cast<int32_t>(samples[1].y)) / 2;
|
||||
const int32_t yMax = (static_cast<int32_t>(samples[2].y) + static_cast<int32_t>(samples[3].y)) / 2;
|
||||
// Targets sit TARGET_MARGIN in from each edge (see getTargetPoint()), not at the screen
|
||||
// edges themselves - xLow/xHigh/yLow/yHigh are raw samples at those inset positions, not
|
||||
// at 0/width or 0/height. Extrapolate them out to the true edges so the saved range (which
|
||||
// lvgl_pointer.h maps onto the full [0, resolution) display range) lines up correctly
|
||||
// across the whole screen instead of being off by a margin's worth of scale and offset.
|
||||
const auto width = lv_obj_get_content_width(root);
|
||||
const auto height = lv_obj_get_content_height(root);
|
||||
const int32_t xSpan = static_cast<int32_t>(width) - 2 * TARGET_MARGIN;
|
||||
const int32_t ySpan = static_cast<int32_t>(height) - 2 * TARGET_MARGIN;
|
||||
|
||||
if (xSpan <= 0 || ySpan <= 0) {
|
||||
lv_label_set_text(titleLabel, "Calibration Failed");
|
||||
lv_label_set_text(hintLabel, "Screen too small. Tap to close.");
|
||||
lv_obj_add_flag(target, LV_OBJ_FLAG_HIDDEN);
|
||||
setResult(Result::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t xMin = xLow - (xHigh - xLow) * TARGET_MARGIN / xSpan;
|
||||
const int32_t xMax = xHigh + (xHigh - xLow) * TARGET_MARGIN / xSpan;
|
||||
const int32_t yMin = yLow - (yHigh - yLow) * TARGET_MARGIN / ySpan;
|
||||
const int32_t yMax = yHigh + (yHigh - yLow) * TARGET_MARGIN / ySpan;
|
||||
|
||||
settings::touch::TouchCalibrationSettings settings = settings::touch::getDefault();
|
||||
settings.enabled = true;
|
||||
@ -87,7 +112,7 @@ class TouchCalibrationApp final : public App {
|
||||
settings.yMin = yMin;
|
||||
settings.yMax = yMax;
|
||||
|
||||
if ((xMax - xMin) < MIN_RANGE || (yMax - yMin) < MIN_RANGE || !settings::touch::isValid(settings)) {
|
||||
if (!settings::touch::isValid(settings)) {
|
||||
lv_label_set_text(titleLabel, "Calibration Failed");
|
||||
lv_label_set_text(hintLabel, "Range invalid. Tap to close.");
|
||||
lv_obj_add_flag(target, LV_OBJ_FLAG_HIDDEN);
|
||||
@ -103,6 +128,20 @@ class TouchCalibrationApp final : public App {
|
||||
return;
|
||||
}
|
||||
|
||||
LvglPointerCalibration calibration = {
|
||||
.x_min = xMin,
|
||||
.x_max = xMax,
|
||||
.y_min = yMin,
|
||||
.y_max = yMax,
|
||||
};
|
||||
lvgl_lock();
|
||||
auto* indev = lvgl_pointer_get_default();
|
||||
if (indev != nullptr) {
|
||||
lvgl_pointer_set_calibration(indev, &calibration);
|
||||
}
|
||||
lvgl_unlock();
|
||||
calibrationApplied = true;
|
||||
|
||||
LOG_I(TAG, "Saved calibration x=[%d, %d] y=[%d, %d]", xMin, xMax, yMin, yMax);
|
||||
lv_label_set_text(titleLabel, "Calibration Complete");
|
||||
lv_label_set_text(hintLabel, "Touch anywhere to continue.");
|
||||
@ -141,14 +180,36 @@ public:
|
||||
|
||||
void onCreate(AppContext& app) override {
|
||||
(void)app;
|
||||
settings::touch::setRuntimeCalibrationEnabled(false);
|
||||
settings::touch::invalidateCache();
|
||||
// Clear any active calibration so the taps sampled below are raw, uncalibrated coordinates.
|
||||
lvgl_lock();
|
||||
auto* indev = lvgl_pointer_get_default();
|
||||
if (indev != nullptr) {
|
||||
lvgl_pointer_set_calibration(indev, nullptr);
|
||||
}
|
||||
lvgl_unlock();
|
||||
}
|
||||
|
||||
void onDestroy(AppContext& app) override {
|
||||
(void)app;
|
||||
settings::touch::setRuntimeCalibrationEnabled(true);
|
||||
settings::touch::invalidateCache();
|
||||
// finishCalibration() already applied a new calibration on success. On cancel/failure,
|
||||
// restore whatever calibration was on disk before onCreate() cleared it above.
|
||||
if (calibrationApplied) {
|
||||
return;
|
||||
}
|
||||
|
||||
settings::touch::TouchCalibrationSettings settings;
|
||||
lvgl_lock();
|
||||
auto* indev = lvgl_pointer_get_default();
|
||||
if (indev != nullptr && settings::touch::load(settings) && settings.enabled && settings::touch::isValid(settings)) {
|
||||
LvglPointerCalibration calibration = {
|
||||
.x_min = settings.xMin,
|
||||
.x_max = settings.xMax,
|
||||
.y_min = settings.yMin,
|
||||
.y_max = settings.yMax,
|
||||
};
|
||||
lvgl_pointer_set_calibration(indev, &calibration);
|
||||
}
|
||||
lvgl_unlock();
|
||||
}
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
@ -196,9 +257,11 @@ public:
|
||||
extern const AppManifest manifest = {
|
||||
.appId = "TouchCalibration",
|
||||
.appName = "Touch Calibration",
|
||||
.appCategory = Category::System,
|
||||
.appFlags = AppManifest::Flags::HideStatusBar | AppManifest::Flags::Hidden,
|
||||
.appCategory = Category::Settings,
|
||||
.appFlags = AppManifest::Flags::HideStatusBar,
|
||||
.createApp = create<TouchCalibrationApp>
|
||||
};
|
||||
|
||||
} // namespace tt::app::touchcalibration
|
||||
|
||||
#endif // defined(CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED)
|
||||
|
||||
@ -8,11 +8,13 @@
|
||||
#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>
|
||||
@ -71,6 +73,24 @@ void attachDevices() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@ -2,10 +2,8 @@
|
||||
|
||||
#include <Tactility/file/File.h>
|
||||
#include <Tactility/file/PropertiesFile.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
#include <Tactility/Paths.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
@ -24,11 +22,6 @@ constexpr auto* SETTINGS_KEY_X_MAX = "xMax";
|
||||
constexpr auto* SETTINGS_KEY_Y_MIN = "yMin";
|
||||
constexpr auto* SETTINGS_KEY_Y_MAX = "yMax";
|
||||
|
||||
static bool runtimeCalibrationEnabled = true;
|
||||
static bool cacheInitialized = false;
|
||||
static TouchCalibrationSettings cachedSettings;
|
||||
static tt::Mutex cacheMutex;
|
||||
|
||||
static bool toBool(const std::string& value) {
|
||||
return value == "1" || value == "true" || value == "True";
|
||||
}
|
||||
@ -127,62 +120,7 @@ bool save(const TouchCalibrationSettings& settings) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file::savePropertiesFile(settings_path, map)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
cachedSettings = settings;
|
||||
cacheInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
TouchCalibrationSettings getActive() {
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
if (!cacheInitialized) {
|
||||
cachedSettings = loadOrGetDefault();
|
||||
cacheInitialized = true;
|
||||
}
|
||||
if (!runtimeCalibrationEnabled) {
|
||||
auto disabled = cachedSettings;
|
||||
disabled.enabled = false;
|
||||
return disabled;
|
||||
}
|
||||
return cachedSettings;
|
||||
}
|
||||
|
||||
void setRuntimeCalibrationEnabled(bool enabled) {
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
runtimeCalibrationEnabled = enabled;
|
||||
}
|
||||
|
||||
void invalidateCache() {
|
||||
auto lock = cacheMutex.asScopedLock();
|
||||
lock.lock();
|
||||
cacheInitialized = false;
|
||||
}
|
||||
|
||||
bool applyCalibration(const TouchCalibrationSettings& settings, uint16_t xMax, uint16_t yMax, uint16_t& x, uint16_t& y) {
|
||||
if (!settings.enabled || !isValid(settings)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t in_x = static_cast<int32_t>(x);
|
||||
const int32_t in_y = static_cast<int32_t>(y);
|
||||
|
||||
const int64_t mapped_x = (static_cast<int64_t>(in_x) - static_cast<int64_t>(settings.xMin)) *
|
||||
static_cast<int64_t>(xMax) /
|
||||
(static_cast<int64_t>(settings.xMax) - static_cast<int64_t>(settings.xMin));
|
||||
const int64_t mapped_y = (static_cast<int64_t>(in_y) - static_cast<int64_t>(settings.yMin)) *
|
||||
static_cast<int64_t>(yMax) /
|
||||
(static_cast<int64_t>(settings.yMax) - static_cast<int64_t>(settings.yMin));
|
||||
|
||||
x = static_cast<uint16_t>(std::clamp<int64_t>(mapped_x, 0, static_cast<int64_t>(xMax)));
|
||||
y = static_cast<uint16_t>(std::clamp<int64_t>(mapped_y, 0, static_cast<int64_t>(yMax)));
|
||||
return true;
|
||||
return file::savePropertiesFile(settings_path, map);
|
||||
}
|
||||
|
||||
} // namespace tt::settings::touch
|
||||
|
||||
87
device.py
87
device.py
@ -62,20 +62,19 @@ def has_group(properties: dict, group: str):
|
||||
prefix = f"{group}."
|
||||
return any(key.startswith(prefix) for key in properties)
|
||||
|
||||
def get_property_or_exit(properties: dict, group: str, key: str):
|
||||
full_key = f"{group}.{key}"
|
||||
if full_key not in properties:
|
||||
exit_with_error(f"Device properties does not contain key: {full_key}")
|
||||
return properties[full_key]
|
||||
def get_property_or_exit(properties: dict, key: str):
|
||||
if key not in properties:
|
||||
exit_with_error(f"Device properties does not contain key: {key}")
|
||||
return properties[key]
|
||||
|
||||
def get_property_or_default(properties: dict, group: str, key: str, default):
|
||||
return properties.get(f"{group}.{key}", default)
|
||||
def get_property_or_default(properties: dict, key: str, default):
|
||||
return properties.get(key, default)
|
||||
|
||||
def get_property_or_none(properties: dict, group: str, key: str):
|
||||
return get_property_or_default(properties, group, key, None)
|
||||
def get_property_or_none(properties: dict, key: str):
|
||||
return get_property_or_default(properties, key, None)
|
||||
|
||||
def get_boolean_property_or_false(properties: dict, group: str, key: str):
|
||||
return properties.get(f"{group}.{key}") == "true"
|
||||
def get_boolean_property_or_false(properties: dict, key: str):
|
||||
return properties.get(key) == "true"
|
||||
|
||||
def safe_int(value: str, error_message: str):
|
||||
try:
|
||||
@ -95,13 +94,13 @@ def write_defaults(output_file):
|
||||
output_file.write(default_properties)
|
||||
|
||||
def get_user_data_location(device_properties: dict):
|
||||
user_data_location = get_property_or_exit(device_properties, "storage", "userDataLocation")
|
||||
user_data_location = get_property_or_exit(device_properties, "storage.userDataLocation")
|
||||
if user_data_location not in ("SD", "Internal"):
|
||||
exit_with_error(f"storage.userDataLocation must be 'SD' or 'Internal', but was: '{user_data_location}'")
|
||||
return user_data_location
|
||||
|
||||
def write_partition_table(output_file, device_properties: dict, is_dev: bool):
|
||||
flash_size = get_property_or_exit(device_properties, "hardware", "flashSize")
|
||||
flash_size = get_property_or_exit(device_properties, "hardware.flashSize")
|
||||
if not flash_size.endswith("MB"):
|
||||
exit_with_error("Flash size should be written as xMB or xxMB (e.g. 4MB, 16MB)")
|
||||
flash_size_number = flash_size[:-2]
|
||||
@ -122,8 +121,8 @@ def write_partition_table(output_file, device_properties: dict, is_dev: bool):
|
||||
|
||||
def write_tactility_variables(output_file, device_properties: dict, device_id: str):
|
||||
# Board and vendor
|
||||
board_vendor = get_property_or_exit(device_properties, "general", "vendor").replace("\"", "\\\"")
|
||||
board_name = get_property_or_exit(device_properties, "general", "name").replace("\"", "\\\"")
|
||||
board_vendor = get_property_or_exit(device_properties, "general.vendor").replace("\"", "\\\"")
|
||||
board_name = get_property_or_exit(device_properties, "general.name").replace("\"", "\\\"")
|
||||
if board_name == board_vendor or board_vendor == "":
|
||||
output_file.write(f"CONFIG_TT_DEVICE_NAME=\"{board_name}\"\n")
|
||||
else:
|
||||
@ -134,10 +133,10 @@ def write_tactility_variables(output_file, device_properties: dict, device_id: s
|
||||
if device_id == "lilygo-tdeck":
|
||||
output_file.write("CONFIG_TT_TDECK_WORKAROUND=y\n")
|
||||
# Launcher app id
|
||||
launcher_app_id = get_property_or_exit(device_properties, "apps", "launcherAppId").replace("\"", "\\\"")
|
||||
launcher_app_id = get_property_or_exit(device_properties, "apps.launcherAppId").replace("\"", "\\\"")
|
||||
output_file.write(f"CONFIG_TT_LAUNCHER_APP_ID=\"{launcher_app_id}\"\n")
|
||||
# Auto start app id
|
||||
auto_start_app_id = get_property_or_none(device_properties, "apps", "autoStartAppId")
|
||||
auto_start_app_id = get_property_or_none(device_properties, "apps.autoStartAppId")
|
||||
if auto_start_app_id is not None:
|
||||
safe_auto_start_app_id = auto_start_app_id.replace("\"", "\\\"")
|
||||
output_file.write(f"CONFIG_TT_AUTO_START_APP_ID=\"{safe_auto_start_app_id}\"\n")
|
||||
@ -148,7 +147,7 @@ def write_tactility_variables(output_file, device_properties: dict, device_id: s
|
||||
output_file.write("CONFIG_TT_USER_DATA_LOCATION_INTERNAL=y\n")
|
||||
|
||||
def write_core_variables(output_file, device_properties: dict):
|
||||
idf_target = get_property_or_exit(device_properties, "hardware", "target").lower()
|
||||
idf_target = get_property_or_exit(device_properties, "hardware.target").lower()
|
||||
output_file.write("# Target\n")
|
||||
output_file.write(f"CONFIG_IDF_TARGET=\"{idf_target}\"\n")
|
||||
output_file.write("# CPU\n")
|
||||
@ -171,28 +170,28 @@ def write_core_variables(output_file, device_properties: dict):
|
||||
output_file.write("CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=y\n")
|
||||
output_file.write("CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=y\n")
|
||||
# Usage of tt::hal can be disabled to simplify dependency wiring (device depends on TactilityKernel instead of Tactility)
|
||||
use_deprecated_hal = get_property_or_none(device_properties, "dependencies", "useDeprecatedHal")
|
||||
use_deprecated_hal = get_property_or_none(device_properties, "dependencies.useDeprecatedHal")
|
||||
if use_deprecated_hal is None or use_deprecated_hal.lower() == "true":
|
||||
output_file.write("CONFIG_TT_USE_DEPRECATED_HAL=y\n")
|
||||
else:
|
||||
output_file.write("CONFIG_TT_USE_DEPRECATED_HAL=n\n")
|
||||
|
||||
def write_flash_variables(output_file, device_properties: dict):
|
||||
flash_size = get_property_or_exit(device_properties, "hardware", "flashSize")
|
||||
flash_size = get_property_or_exit(device_properties, "hardware.flashSize")
|
||||
if not flash_size.endswith("MB"):
|
||||
exit_with_error("Flash size should be written as xMB or xxMB (e.g. 4MB, 16MB)")
|
||||
output_file.write("# Flash\n")
|
||||
flash_size_number = flash_size[:-2]
|
||||
output_file.write(f"CONFIG_ESPTOOLPY_FLASHSIZE_{flash_size_number}MB=y\n")
|
||||
flash_mode = get_property_or_default(device_properties, "hardware", "flashMode", 'QIO')
|
||||
flash_mode = get_property_or_default(device_properties, "hardware.flashMode", 'QIO')
|
||||
output_file.write(f"CONFIG_FLASHMODE_{flash_mode}=y\n")
|
||||
esptool_flash_freq = get_property_or_none(device_properties, "hardware", "esptoolFlashFreq")
|
||||
esptool_flash_freq = get_property_or_none(device_properties, "hardware.esptoolFlashFreq")
|
||||
if esptool_flash_freq is not None:
|
||||
output_file.write(f"CONFIG_ESPTOOLPY_FLASHFREQ_{esptool_flash_freq}=y\n")
|
||||
|
||||
def write_spiram_variables(output_file, device_properties: dict):
|
||||
idf_target = get_property_or_exit(device_properties, "hardware", "target").lower()
|
||||
has_spiram = get_property_or_exit(device_properties, "hardware", "spiRam")
|
||||
idf_target = get_property_or_exit(device_properties, "hardware.target").lower()
|
||||
has_spiram = get_property_or_exit(device_properties, "hardware.spiRam")
|
||||
if has_spiram != "true":
|
||||
return
|
||||
output_file.write("# SPIRAM\n")
|
||||
@ -201,7 +200,7 @@ def write_spiram_variables(output_file, device_properties: dict):
|
||||
# Enable
|
||||
output_file.write("CONFIG_SPIRAM=y\n")
|
||||
output_file.write(f"CONFIG_{idf_target.upper()}_SPIRAM_SUPPORT=y\n")
|
||||
mode = get_property_or_exit(device_properties, "hardware", "spiRamMode")
|
||||
mode = get_property_or_exit(device_properties, "hardware.spiRamMode")
|
||||
if mode == "OPI":
|
||||
mode = "OCT"
|
||||
# Mode
|
||||
@ -209,7 +208,7 @@ def write_spiram_variables(output_file, device_properties: dict):
|
||||
output_file.write(f"CONFIG_SPIRAM_MODE_{mode}=y\n")
|
||||
else:
|
||||
output_file.write("CONFIG_SPIRAM_TYPE_AUTO=y\n")
|
||||
speed = get_property_or_exit(device_properties, "hardware", "spiRamSpeed")
|
||||
speed = get_property_or_exit(device_properties, "hardware.spiRamSpeed")
|
||||
# Speed
|
||||
output_file.write(f"CONFIG_SPIRAM_SPEED_{speed}=y\n")
|
||||
output_file.write(f"CONFIG_SPIRAM_SPEED={speed}\n")
|
||||
@ -223,7 +222,7 @@ def write_spiram_variables(output_file, device_properties: dict):
|
||||
output_file.write("CONFIG_SPIRAM_XIP_FROM_PSRAM=y\n")
|
||||
|
||||
def write_performance_improvements(output_file, device_properties: dict):
|
||||
idf_target = get_property_or_exit(device_properties, "hardware", "target").lower()
|
||||
idf_target = get_property_or_exit(device_properties, "hardware.target").lower()
|
||||
if idf_target == "esp32s3":
|
||||
output_file.write("# Performance improvement: Fixes glitches in the RGB display driver when rendering new screens/apps\n")
|
||||
output_file.write("CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y\n")
|
||||
@ -246,16 +245,16 @@ def write_lvgl_variables(output_file, device_properties: dict):
|
||||
write_lvgl_variable_placeholders(output_file)
|
||||
return
|
||||
# LVGL DPI overrides the real DPI settings
|
||||
dpi_text = get_property_or_none(device_properties, "lvgl", "dpi")
|
||||
dpi_text = get_property_or_none(device_properties, "lvgl.dpi")
|
||||
if dpi_text is None:
|
||||
dpi_text = get_property_or_exit(device_properties, "display", "dpi")
|
||||
dpi_text = get_property_or_exit(device_properties, "display.dpi")
|
||||
dpi = safe_int(dpi_text, f"DPI must be an integer, but was: '{dpi_text}'")
|
||||
output_file.write(f"CONFIG_LV_DPI_DEF={dpi}\n")
|
||||
color_depth = get_property_or_exit(device_properties, "lvgl", "colorDepth")
|
||||
color_depth = get_property_or_exit(device_properties, "lvgl.colorDepth")
|
||||
output_file.write(f"CONFIG_LV_COLOR_DEPTH={color_depth}\n")
|
||||
output_file.write(f"CONFIG_LV_COLOR_DEPTH_{color_depth}=y\n")
|
||||
output_file.write("CONFIG_LV_DISP_DEF_REFR_PERIOD=10\n")
|
||||
theme = get_property_or_default(device_properties, "lvgl", "theme", "DefaultDark")
|
||||
theme = get_property_or_default(device_properties, "lvgl.theme", "DefaultDark")
|
||||
if theme == "DefaultDark":
|
||||
output_file.write("CONFIG_LV_THEME_DEFAULT_DARK=y\n")
|
||||
elif theme == "DefaultLight":
|
||||
@ -264,7 +263,7 @@ def write_lvgl_variables(output_file, device_properties: dict):
|
||||
output_file.write("CONFIG_LV_USE_THEME_MONO=y\n")
|
||||
else:
|
||||
exit_with_error(f"Unknown theme: {theme}")
|
||||
font_height_text = get_property_or_default(device_properties, "lvgl", "fontSize", "14")
|
||||
font_height_text = get_property_or_default(device_properties, "lvgl.fontSize", "14")
|
||||
font_height = safe_int(font_height_text, f"Font height must be an integer, but was: '{font_height_text}'")
|
||||
if font_height <= 12:
|
||||
output_file.write("CONFIG_LV_FONT_MONTSERRAT_8=y\n")
|
||||
@ -333,14 +332,23 @@ def write_lvgl_variables(output_file, device_properties: dict):
|
||||
output_file.write("CONFIG_TT_LVGL_LAUNCHER_ICON_SIZE=72\n")
|
||||
output_file.write("CONFIG_TT_LVGL_SHARED_ICON_SIZE=32\n")
|
||||
|
||||
def write_touch_calibration_variables(output_file, device_properties: dict):
|
||||
calibration_supported = get_property_or_none(device_properties, "touch.calibrationSupported")
|
||||
if calibration_supported is not None and calibration_supported.lower() == "true":
|
||||
output_file.write("# Touch calibration\n")
|
||||
output_file.write("CONFIG_TT_TOUCH_CALIBRATION_SUPPORTED=y\n")
|
||||
calibration_required = get_property_or_none(device_properties, "touch.calibrationRequired")
|
||||
if calibration_required is not None and calibration_required.lower() == "true":
|
||||
output_file.write("CONFIG_TT_TOUCH_CALIBRATION_REQUIRED=y\n")
|
||||
|
||||
|
||||
def write_usb_variables(output_file, device_properties: dict):
|
||||
has_tiny_usb = get_boolean_property_or_false(device_properties, "hardware", "tinyUsb")
|
||||
has_tiny_usb = get_boolean_property_or_false(device_properties, "hardware.tinyUsb")
|
||||
if has_tiny_usb:
|
||||
output_file.write("# TinyUSB\n")
|
||||
output_file.write("CONFIG_TINYUSB_MSC_ENABLED=y\n")
|
||||
output_file.write("CONFIG_TINYUSB_MSC_MOUNT_PATH=\"/sdcard\"\n")
|
||||
idf_target = get_property_or_exit(device_properties, "hardware", "target").lower()
|
||||
idf_target = get_property_or_exit(device_properties, "hardware.target").lower()
|
||||
if idf_target == "esp32p4":
|
||||
# P4 has two USB-DWC controllers (HS/UTMI and FS/FSLS). esp_tinyusb defaults to
|
||||
# RHPORT_HS (UTMI), which is the same controller claimed by usbhost0's
|
||||
@ -349,8 +357,8 @@ def write_usb_variables(output_file, device_properties: dict):
|
||||
output_file.write("CONFIG_TINYUSB_RHPORT_FS=y\n")
|
||||
|
||||
def write_bluetooth_variables(output_file, device_properties: dict):
|
||||
idf_target = get_property_or_exit(device_properties, "hardware", "target").lower()
|
||||
has_bluetooth = get_boolean_property_or_false(device_properties, "hardware", "bluetooth")
|
||||
idf_target = get_property_or_exit(device_properties, "hardware.target").lower()
|
||||
has_bluetooth = get_boolean_property_or_false(device_properties, "hardware.bluetooth")
|
||||
if has_bluetooth:
|
||||
output_file.write("# Bluetooth (NimBLE)\n")
|
||||
output_file.write("CONFIG_BT_ENABLED=y\n")
|
||||
@ -365,7 +373,7 @@ def write_bluetooth_variables(output_file, device_properties: dict):
|
||||
# and does not suffer from the same fragmentation — enabling reliable re-init.
|
||||
# Also frees significant internal RAM on memory-constrained targets (e.g. S3).
|
||||
# Dependency: CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC (set by write_spiram_variables).
|
||||
has_spiram = get_boolean_property_or_false(device_properties, "hardware", "spiRam")
|
||||
has_spiram = get_boolean_property_or_false(device_properties, "hardware.spiRam")
|
||||
if has_spiram:
|
||||
output_file.write("CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL=y\n")
|
||||
# Expand NimBLE's GAP device name buffer to match BLE_DEVICE_NAME_MAX.
|
||||
@ -382,7 +390,7 @@ def write_bluetooth_variables(output_file, device_properties: dict):
|
||||
output_file.write("CONFIG_BT_NIMBLE_NVS_PERSIST=y\n")
|
||||
|
||||
def write_usbhost_variables(output_file, device_properties: dict):
|
||||
has_usbhost = get_boolean_property_or_false(device_properties, "hardware", "usbHostEnabled")
|
||||
has_usbhost = get_boolean_property_or_false(device_properties, "hardware.usbHostEnabled")
|
||||
if has_usbhost:
|
||||
output_file.write("# USB Host\n")
|
||||
output_file.write("CONFIG_FATFS_VOLUME_COUNT=6\n")
|
||||
@ -416,6 +424,7 @@ def write_properties(output_file, device_properties: dict, device_id: str, is_de
|
||||
write_usbhost_variables(output_file, device_properties)
|
||||
write_custom_sdkconfig(output_file, device_properties)
|
||||
write_lvgl_variables(output_file, device_properties)
|
||||
write_touch_calibration_variables(output_file, device_properties)
|
||||
|
||||
def get_current_sdkconfig_target(sdkconfig_path: str):
|
||||
if not os.path.isfile(sdkconfig_path):
|
||||
@ -448,7 +457,7 @@ def main(device_id: str, is_dev: bool):
|
||||
output_file_path = "sdkconfig"
|
||||
# Clean build dirs if target changes
|
||||
device_properties = read_device_properties(device_id)
|
||||
new_target = get_property_or_exit(device_properties, "hardware", "target").lower()
|
||||
new_target = get_property_or_exit(device_properties, "hardware.target").lower()
|
||||
sdkconfig_target = get_current_sdkconfig_target(output_file_path)
|
||||
clean_build_dirs_on_platform_change(sdkconfig_target, new_target)
|
||||
if os.path.isfile(output_file_path):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user