mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-18 17:35:05 +00:00
Implement device management (#199)
- Added `tt::hal::Device` and functions (de)register devices and search for them. - Refactored apps: `Power` and `Display` settings apps now use the device API to find devices. - Implemented the new API for all existing drivers for all devices, including the simulator. - Updated HAL Configuration to return `std::shared_ptr` instead of raw pointers. - Added test project for headless tests and implemented tests for the new code.
This commit is contained in:
parent
c87200a80d
commit
cff0605b0a
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@ -19,5 +19,7 @@ jobs:
|
|||||||
run: cmake -S ./ -B build
|
run: cmake -S ./ -B build
|
||||||
- name: "Build Tests"
|
- name: "Build Tests"
|
||||||
run: cmake --build build --target build-tests
|
run: cmake --build build --target build-tests
|
||||||
- name: "Run Tests"
|
- name: "Run TactilityCore Tests"
|
||||||
run: build/Tests/TactilityCore/TactilityCoreTests --exit
|
run: build/Tests/TactilityCore/TactilityCoreTests --exit
|
||||||
|
- name: "Run TactilityHeadless Tests"
|
||||||
|
run: build/Tests/TactilityHeadless/TactilityHeadlessTests --exit
|
||||||
|
|||||||
@ -205,10 +205,10 @@ void YellowDisplay::setGammaCurve(uint8_t index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable YellowDisplay::createTouch() {
|
std::shared_ptr<tt::hal::Touch> _Nullable YellowDisplay::createTouch() {
|
||||||
return static_cast<tt::hal::Touch*>(new YellowTouch());
|
return std::make_shared<YellowTouch>();
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Display* createDisplay() {
|
std::shared_ptr<tt::hal::Display> createDisplay() {
|
||||||
return static_cast<tt::hal::Display*>(new YellowDisplay());
|
return std::make_shared<YellowDisplay>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,11 +16,14 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "ILI9341"; }
|
||||||
|
std::string getDescription() const final { return "SPI display"; }
|
||||||
|
|
||||||
bool start() override;
|
bool start() override;
|
||||||
|
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable createTouch() override;
|
std::shared_ptr<tt::hal::Touch> _Nullable createTouch() override;
|
||||||
|
|
||||||
void setBacklightDuty(uint8_t backlightDuty) override;
|
void setBacklightDuty(uint8_t backlightDuty) override;
|
||||||
bool supportsBacklightDuty() const override { return true; }
|
bool supportsBacklightDuty() const override { return true; }
|
||||||
@ -31,4 +34,4 @@ public:
|
|||||||
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Display* createDisplay();
|
std::shared_ptr<tt::hal::Display> createDisplay();
|
||||||
|
|||||||
@ -5,12 +5,20 @@
|
|||||||
#include <esp_lcd_touch.h>
|
#include <esp_lcd_touch.h>
|
||||||
|
|
||||||
class YellowTouch : public tt::hal::Touch {
|
class YellowTouch : public tt::hal::Touch {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
esp_lcd_panel_io_handle_t ioHandle = nullptr;
|
esp_lcd_panel_io_handle_t ioHandle = nullptr;
|
||||||
esp_lcd_touch_handle_t touchHandle = nullptr;
|
esp_lcd_touch_handle_t touchHandle = nullptr;
|
||||||
lv_indev_t* _Nullable deviceHandle = nullptr;
|
lv_indev_t* _Nullable deviceHandle = nullptr;
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "CST816S"; }
|
||||||
|
std::string getDescription() const final { return "I2C touch driver"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override;
|
bool start(lv_display_t* display) override;
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
||||||
|
|||||||
@ -191,8 +191,8 @@ void TdeckDisplay::setPowerOn(bool turnOn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable TdeckDisplay::createTouch() {
|
std::shared_ptr<tt::hal::Touch> _Nullable TdeckDisplay::createTouch() {
|
||||||
return static_cast<tt::hal::Touch*>(new TdeckTouch());
|
return std::make_shared<TdeckTouch>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TdeckDisplay::setBacklightDuty(uint8_t backlightDuty) {
|
void TdeckDisplay::setBacklightDuty(uint8_t backlightDuty) {
|
||||||
@ -233,6 +233,6 @@ void TdeckDisplay::setGammaCurve(uint8_t index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Display* createDisplay() {
|
std::shared_ptr<tt::hal::Display> createDisplay() {
|
||||||
return static_cast<tt::hal::Display*>(new TdeckDisplay());
|
return std::make_shared<TdeckDisplay>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,9 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "ST7780"; }
|
||||||
|
std::string getDescription() const final { return "SPI display"; }
|
||||||
|
|
||||||
bool start() override;
|
bool start() override;
|
||||||
|
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
@ -25,7 +28,7 @@ public:
|
|||||||
bool isPoweredOn() const override { return poweredOn; };
|
bool isPoweredOn() const override { return poweredOn; };
|
||||||
bool supportsPowerControl() const override { return true; }
|
bool supportsPowerControl() const override { return true; }
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable createTouch() override;
|
std::shared_ptr<tt::hal::Touch> _Nullable createTouch() override;
|
||||||
|
|
||||||
void setBacklightDuty(uint8_t backlightDuty) override;
|
void setBacklightDuty(uint8_t backlightDuty) override;
|
||||||
bool supportsBacklightDuty() const override { return true; }
|
bool supportsBacklightDuty() const override { return true; }
|
||||||
@ -40,4 +43,4 @@ private:
|
|||||||
static bool startBacklight();
|
static bool startBacklight();
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Display* createDisplay();
|
std::shared_ptr<tt::hal::Display> createDisplay();
|
||||||
|
|||||||
@ -62,6 +62,6 @@ bool TdeckKeyboard::isAttached() const {
|
|||||||
return tt::hal::i2c::masterHasDeviceAtAddress(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, 100);
|
return tt::hal::i2c::masterHasDeviceAtAddress(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Keyboard* createKeyboard() {
|
std::shared_ptr<tt::hal::Keyboard> createKeyboard() {
|
||||||
return dynamic_cast<tt::hal::Keyboard*>(new TdeckKeyboard());
|
return std::make_shared<TdeckKeyboard>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,20 @@
|
|||||||
#include <esp_lcd_touch.h>
|
#include <esp_lcd_touch.h>
|
||||||
|
|
||||||
class TdeckKeyboard : public tt::hal::Keyboard {
|
class TdeckKeyboard : public tt::hal::Keyboard {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
lv_indev_t* _Nullable deviceHandle = nullptr;
|
lv_indev_t* _Nullable deviceHandle = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "T-Deck Keyboard"; }
|
||||||
|
std::string getDescription() const final { return "I2C keyboard"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override;
|
bool start(lv_display_t* display) override;
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
bool isAttached() const override;
|
bool isAttached() const override;
|
||||||
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Keyboard* createKeyboard();
|
std::shared_ptr<tt::hal::Keyboard> createKeyboard();
|
||||||
|
|||||||
@ -15,6 +15,9 @@ public:
|
|||||||
TdeckPower();
|
TdeckPower();
|
||||||
~TdeckPower();
|
~TdeckPower();
|
||||||
|
|
||||||
|
std::string getName() const final { return "ADC Power Measurement"; }
|
||||||
|
std::string getDescription() const final { return "Power measurement interface via ADC pin"; }
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
bool supportsMetric(MetricType type) const override;
|
||||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||||
|
|
||||||
|
|||||||
@ -6,12 +6,19 @@
|
|||||||
#include <esp_lcd_touch.h>
|
#include <esp_lcd_touch.h>
|
||||||
|
|
||||||
class TdeckTouch : public tt::hal::Touch {
|
class TdeckTouch : public tt::hal::Touch {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
std::string getName() const final { return "GT911"; }
|
||||||
|
std::string getDescription() const final { return "I2C Touch Driver"; }
|
||||||
|
|
||||||
esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr;
|
esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr;
|
||||||
esp_lcd_touch_handle_t _Nullable touchHandle = nullptr;
|
esp_lcd_touch_handle_t _Nullable touchHandle = nullptr;
|
||||||
lv_indev_t* _Nullable deviceHandle = nullptr;
|
lv_indev_t* _Nullable deviceHandle = nullptr;
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
bool start(lv_display_t* display) override;
|
bool start(lv_display_t* display) override;
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
||||||
|
|||||||
@ -153,10 +153,10 @@ void Core2Display::setGammaCurve(uint8_t index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable Core2Display::createTouch() {
|
std::shared_ptr<tt::hal::Touch> _Nullable Core2Display::createTouch() {
|
||||||
return static_cast<tt::hal::Touch*>(new Core2Touch());
|
return std::make_shared<Core2Touch>();
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Display* createDisplay() {
|
std::shared_ptr<tt::hal::Display> createDisplay() {
|
||||||
return static_cast<tt::hal::Display*>(new Core2Display());
|
return std::make_shared<Core2Display>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,11 +17,14 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "ILI9342C"; }
|
||||||
|
std::string getDescription() const final { return "Display (ILI9342C with an ILI9341 driver)"; }
|
||||||
|
|
||||||
bool start() override;
|
bool start() override;
|
||||||
|
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable createTouch() override;
|
std::shared_ptr<tt::hal::Touch> _Nullable createTouch() override;
|
||||||
|
|
||||||
bool supportsBacklightDuty() const override { return false; }
|
bool supportsBacklightDuty() const override { return false; }
|
||||||
|
|
||||||
@ -31,4 +34,4 @@ public:
|
|||||||
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Display* createDisplay();
|
std::shared_ptr<tt::hal::Display> createDisplay();
|
||||||
|
|||||||
@ -12,6 +12,9 @@ public:
|
|||||||
Core2Power() = default;
|
Core2Power() = default;
|
||||||
~Core2Power() override = default;
|
~Core2Power() override = default;
|
||||||
|
|
||||||
|
std::string getName() const final { return "AXP192 Power"; }
|
||||||
|
std::string getDescription() const final { return "Power management via I2C"; }
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
bool supportsMetric(MetricType type) const override;
|
||||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,9 @@ public:
|
|||||||
|
|
||||||
Core2Touch();
|
Core2Touch();
|
||||||
|
|
||||||
|
std::string getName() const final { return "FT6336U"; }
|
||||||
|
std::string getDescription() const final { return "I2C touch driver"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override;
|
bool start(lv_display_t* display) override;
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,15 @@
|
|||||||
|
|
||||||
#define AW9523_ADDRESS 0x58
|
#define AW9523_ADDRESS 0x58
|
||||||
|
|
||||||
class Aw9523 : I2cDevice {
|
class Aw9523 : public tt::hal::i2c::I2cDevice {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit Aw9523(i2c_port_t port) : I2cDevice(port, AW9523_ADDRESS) {}
|
explicit Aw9523(i2c_port_t port) : I2cDevice(port, AW9523_ADDRESS) {}
|
||||||
|
|
||||||
|
std::string getName() const final { return "AW9523"; }
|
||||||
|
std::string getDescription() const final { return "GPIO expander with LED driver and I2C interface."; }
|
||||||
|
|
||||||
bool readP0(uint8_t& output) const;
|
bool readP0(uint8_t& output) const;
|
||||||
bool readP1(uint8_t& output) const;
|
bool readP1(uint8_t& output) const;
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
* - https://github.com/m5stack/M5Unified/blob/master/src/utility/AXP2101_Class.cpp
|
* - https://github.com/m5stack/M5Unified/blob/master/src/utility/AXP2101_Class.cpp
|
||||||
* - http://file.whycan.com/files/members/6736/AXP2101_Datasheet_V1.0_en_3832.pdf
|
* - http://file.whycan.com/files/members/6736/AXP2101_Datasheet_V1.0_en_3832.pdf
|
||||||
*/
|
*/
|
||||||
class Axp2101 : I2cDevice {
|
class Axp2101 final : public tt::hal::i2c::I2cDevice {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -21,6 +21,9 @@ public:
|
|||||||
|
|
||||||
explicit Axp2101(i2c_port_t port) : I2cDevice(port, AXP2101_ADDRESS) {}
|
explicit Axp2101(i2c_port_t port) : I2cDevice(port, AXP2101_ADDRESS) {}
|
||||||
|
|
||||||
|
std::string getName() const final { return "AXP2101"; }
|
||||||
|
std::string getDescription() const final { return "Power management with I2C interface."; }
|
||||||
|
|
||||||
bool setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const;
|
bool setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const;
|
||||||
|
|
||||||
bool getBatteryVoltage(float& vbatMillis) const;
|
bool getBatteryVoltage(float& vbatMillis) const;
|
||||||
|
|||||||
@ -181,10 +181,10 @@ void CoreS3Display::setBacklightDuty(uint8_t backlightDuty) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable CoreS3Display::createTouch() {
|
std::shared_ptr<tt::hal::Touch> _Nullable CoreS3Display::createTouch() {
|
||||||
return static_cast<tt::hal::Touch*>(new CoreS3Touch());
|
return std::make_shared<CoreS3Touch>();
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Display* createDisplay() {
|
std::shared_ptr<tt::hal::Display> createDisplay() {
|
||||||
return static_cast<tt::hal::Display*>(new CoreS3Display());
|
return std::make_shared<CoreS3Display>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,11 +16,14 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "ILI9342C"; }
|
||||||
|
std::string getDescription() const final { return "Display (ILI9342C with an ILI9341 driver)"; }
|
||||||
|
|
||||||
bool start() override;
|
bool start() override;
|
||||||
|
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable createTouch() override;
|
std::shared_ptr<tt::hal::Touch> _Nullable createTouch() override;
|
||||||
|
|
||||||
void setBacklightDuty(uint8_t backlightDuty) override;
|
void setBacklightDuty(uint8_t backlightDuty) override;
|
||||||
bool supportsBacklightDuty() const override { return true; }
|
bool supportsBacklightDuty() const override { return true; }
|
||||||
@ -31,4 +34,4 @@ public:
|
|||||||
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Display* createDisplay();
|
std::shared_ptr<tt::hal::Display> createDisplay();
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
#include "CoreS3Power.h"
|
#include "CoreS3Power.h"
|
||||||
#include <Tactility/TactilityCore.h>
|
|
||||||
|
|
||||||
#define TAG "core2_power"
|
#define TAG "core2_power"
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,9 @@ public:
|
|||||||
CoreS3Power() = default;
|
CoreS3Power() = default;
|
||||||
~CoreS3Power() override = default;
|
~CoreS3Power() override = default;
|
||||||
|
|
||||||
|
std::string getName() const final { return "AXP2101 Power"; }
|
||||||
|
std::string getDescription() const final { return "Power management via I2C"; }
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
bool supportsMetric(MetricType type) const override;
|
||||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,20 @@
|
|||||||
#include <esp_lcd_touch.h>
|
#include <esp_lcd_touch.h>
|
||||||
|
|
||||||
class CoreS3Touch : public tt::hal::Touch {
|
class CoreS3Touch : public tt::hal::Touch {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
esp_lcd_panel_io_handle_t ioHandle = nullptr;
|
esp_lcd_panel_io_handle_t ioHandle = nullptr;
|
||||||
esp_lcd_touch_handle_t touchHandle = nullptr;
|
esp_lcd_touch_handle_t touchHandle = nullptr;
|
||||||
lv_indev_t* _Nullable deviceHandle = nullptr;
|
lv_indev_t* _Nullable deviceHandle = nullptr;
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "FT6336U"; }
|
||||||
|
std::string getDescription() const final { return "I2C touch driver"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override;
|
bool start(lv_display_t* display) override;
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
||||||
|
|||||||
@ -2,23 +2,28 @@
|
|||||||
|
|
||||||
#include "SdlTouch.h"
|
#include "SdlTouch.h"
|
||||||
#include <Tactility/hal/Display.h>
|
#include <Tactility/hal/Display.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
extern lv_disp_t* displayHandle;
|
extern lv_disp_t* displayHandle;
|
||||||
|
|
||||||
class SdlDisplay : public tt::hal::Display {
|
class SdlDisplay final : public tt::hal::Display {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "SDL Display"; }
|
||||||
|
std::string getDescription() const final { return ""; }
|
||||||
|
|
||||||
bool start() override {
|
bool start() override {
|
||||||
return displayHandle != nullptr;
|
return displayHandle != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool stop() override { tt_crash("Not supported"); }
|
bool stop() override { tt_crash("Not supported"); }
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable createTouch() override { return dynamic_cast<tt::hal::Touch*>(new SdlTouch()); }
|
std::shared_ptr<tt::hal::Touch> _Nullable createTouch() override { return std::make_shared<SdlTouch>(); }
|
||||||
|
|
||||||
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Display* createDisplay() {
|
std::shared_ptr<tt::hal::Display> createDisplay() {
|
||||||
return static_cast<tt::hal::Display*>(new SdlDisplay());
|
return std::make_shared<SdlDisplay>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
#include <Tactility/hal/Keyboard.h>
|
#include <Tactility/hal/Keyboard.h>
|
||||||
#include <Tactility/TactilityCore.h>
|
#include <Tactility/TactilityCore.h>
|
||||||
|
|
||||||
class SdlKeyboard : public tt::hal::Keyboard {
|
class SdlKeyboard final : public tt::hal::Keyboard {
|
||||||
private:
|
private:
|
||||||
lv_indev_t* _Nullable handle = nullptr;
|
lv_indev_t* _Nullable handle = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "SDL Keyboard"; }
|
||||||
|
std::string getDescription() const final { return "SDL keyboard device"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override {
|
bool start(lv_display_t* display) override {
|
||||||
handle = lv_sdl_keyboard_create();
|
handle = lv_sdl_keyboard_create();
|
||||||
return handle != nullptr;
|
return handle != nullptr;
|
||||||
@ -20,6 +24,6 @@ public:
|
|||||||
lv_indev_t* _Nullable getLvglIndev() override { return handle; }
|
lv_indev_t* _Nullable getLvglIndev() override { return handle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Keyboard* createKeyboard() {
|
std::shared_ptr<tt::hal::Keyboard> createKeyboard() {
|
||||||
return static_cast<tt::hal::Keyboard*>(new SdlKeyboard());
|
return std::make_shared<SdlKeyboard>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
#include <Tactility/hal/Touch.h>
|
#include <Tactility/hal/Touch.h>
|
||||||
#include <Tactility/TactilityCore.h>
|
#include <Tactility/TactilityCore.h>
|
||||||
|
|
||||||
class SdlTouch : public tt::hal::Touch {
|
class SdlTouch final : public tt::hal::Touch {
|
||||||
private:
|
private:
|
||||||
lv_indev_t* _Nullable handle = nullptr;
|
lv_indev_t* _Nullable handle = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "SDL Pointer"; }
|
||||||
|
std::string getDescription() const final { return "SDL mouse/touch pointer device"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override {
|
bool start(lv_display_t* display) override {
|
||||||
handle = lv_sdl_mouse_create();
|
handle = lv_sdl_mouse_create();
|
||||||
return handle != nullptr;
|
return handle != nullptr;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using namespace tt::hal;
|
using namespace tt::hal;
|
||||||
|
|
||||||
class SimulatorPower : public Power {
|
class SimulatorPower final : public Power {
|
||||||
|
|
||||||
bool allowedToCharge = false;
|
bool allowedToCharge = false;
|
||||||
|
|
||||||
@ -14,6 +14,9 @@ public:
|
|||||||
SimulatorPower() = default;
|
SimulatorPower() = default;
|
||||||
~SimulatorPower() override = default;
|
~SimulatorPower() override = default;
|
||||||
|
|
||||||
|
std::string getName() const final { return "Power Mock"; }
|
||||||
|
std::string getDescription() const final { return ""; }
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
bool supportsMetric(MetricType type) const override;
|
||||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,24 @@
|
|||||||
|
|
||||||
using namespace tt::hal;
|
using namespace tt::hal;
|
||||||
|
|
||||||
class SimulatorSdCard : public SdCard {
|
class SimulatorSdCard final : public SdCard {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
State state;
|
State state;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SimulatorSdCard() : SdCard(MountBehaviour::AtBoot), state(State::Unmounted) {}
|
SimulatorSdCard() : SdCard(MountBehaviour::AtBoot), state(State::Unmounted) {}
|
||||||
|
|
||||||
|
std::string getName() const final { return "Mock SD Card"; }
|
||||||
|
std::string getDescription() const final { return ""; }
|
||||||
|
|
||||||
bool mount(const char* mountPath) override {
|
bool mount(const char* mountPath) override {
|
||||||
state = State::Mounted;
|
state = State::Mounted;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unmount() override {
|
bool unmount() override {
|
||||||
state = State::Unmounted;
|
state = State::Unmounted;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#define BQ24295_ADDRESS 0x6BU
|
#define BQ24295_ADDRESS 0x6BU
|
||||||
|
|
||||||
class Bq24295 : I2cDevice {
|
class Bq24295 final : public tt::hal::i2c::I2cDevice {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -12,6 +12,10 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "BQ24295"; }
|
||||||
|
|
||||||
|
std::string getDescription() const final { return "I2C-controlled single cell USB charger"; }
|
||||||
|
|
||||||
enum class WatchDogTimer {
|
enum class WatchDogTimer {
|
||||||
Disabled = 0b000000,
|
Disabled = 0b000000,
|
||||||
Enabled40s = 0b010000,
|
Enabled40s = 0b010000,
|
||||||
|
|||||||
@ -30,8 +30,8 @@ bool UnPhoneDisplay::start() {
|
|||||||
lv_display_set_color_format(displayHandle, LV_COLOR_FORMAT_NATIVE);
|
lv_display_set_color_format(displayHandle, LV_COLOR_FORMAT_NATIVE);
|
||||||
|
|
||||||
// TODO malloc to use SPIRAM
|
// TODO malloc to use SPIRAM
|
||||||
static auto* buffer1 = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_SPIRAM);
|
buffer1 = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_SPIRAM);
|
||||||
static auto* buffer2 = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_SPIRAM);
|
buffer2 = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_SPIRAM);
|
||||||
assert(buffer1 != nullptr);
|
assert(buffer1 != nullptr);
|
||||||
assert(buffer2 != nullptr);
|
assert(buffer2 != nullptr);
|
||||||
|
|
||||||
@ -61,13 +61,18 @@ bool UnPhoneDisplay::stop() {
|
|||||||
lv_display_delete(displayHandle);
|
lv_display_delete(displayHandle);
|
||||||
displayHandle = nullptr;
|
displayHandle = nullptr;
|
||||||
|
|
||||||
|
heap_caps_free(buffer1);
|
||||||
|
heap_caps_free(buffer2);
|
||||||
|
buffer1 = nullptr;
|
||||||
|
buffer2 = nullptr;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable UnPhoneDisplay::createTouch() {
|
std::shared_ptr<tt::hal::Touch> _Nullable UnPhoneDisplay::createTouch() {
|
||||||
return static_cast<tt::hal::Touch*>(new UnPhoneTouch());
|
return std::make_shared<UnPhoneTouch>();
|
||||||
}
|
}
|
||||||
|
|
||||||
tt::hal::Display* createDisplay() {
|
std::shared_ptr<tt::hal::Display> createDisplay() {
|
||||||
return static_cast<tt::hal::Display*>(new UnPhoneDisplay());
|
return std::make_shared<UnPhoneDisplay>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,16 +11,21 @@ class UnPhoneDisplay : public tt::hal::Display {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
lv_display_t* displayHandle = nullptr;
|
lv_display_t* displayHandle = nullptr;
|
||||||
|
uint8_t* buffer1 = nullptr;
|
||||||
|
uint8_t* buffer2 = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "HX8357"; }
|
||||||
|
std::string getDescription() const final { return "SPI display"; }
|
||||||
|
|
||||||
bool start() override;
|
bool start() override;
|
||||||
|
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
|
|
||||||
tt::hal::Touch* _Nullable createTouch() override;
|
std::shared_ptr<tt::hal::Touch> _Nullable createTouch() override;
|
||||||
|
|
||||||
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
||||||
};
|
};
|
||||||
|
|
||||||
tt::hal::Display* createDisplay();
|
std::shared_ptr<tt::hal::Display> createDisplay();
|
||||||
|
|||||||
@ -12,6 +12,9 @@ public:
|
|||||||
UnPhonePower() = default;
|
UnPhonePower() = default;
|
||||||
~UnPhonePower() = default;
|
~UnPhonePower() = default;
|
||||||
|
|
||||||
|
std::string getName() const final { return "XPT2046 Power Measurement"; }
|
||||||
|
std::string getDescription() const final { return "Power interface via XPT2046 voltage measurement"; }
|
||||||
|
|
||||||
bool supportsMetric(MetricType type) const override;
|
bool supportsMetric(MetricType type) const override;
|
||||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,9 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
std::string getName() const final { return "XPT2046"; }
|
||||||
|
std::string getDescription() const final { return "I2C touch driver"; }
|
||||||
|
|
||||||
bool start(lv_display_t* display) override;
|
bool start(lv_display_t* display) override;
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
||||||
|
|||||||
@ -3,4 +3,5 @@
|
|||||||
cmake -S ./ -B build-sim
|
cmake -S ./ -B build-sim
|
||||||
cmake --build build-sim --target build-tests -j 14
|
cmake --build build-sim --target build-tests -j 14
|
||||||
build-sim/Tests/TactilityCore/TactilityCoreTests --exit
|
build-sim/Tests/TactilityCore/TactilityCoreTests --exit
|
||||||
|
build-sim/Tests/TactilityHeadless/TactilityHeadlessTests --exit
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# TODOs
|
# TODOs
|
||||||
- Create a base `Driver` object for drives, and a `DriverManager` to find devices
|
- Fix system time to not be 1980 (use build year as minimum)
|
||||||
- Use std::span or string_view in StringUtils https://youtu.be/FRkJCvHWdwQ?t=2754
|
- Use std::span or string_view in StringUtils https://youtu.be/FRkJCvHWdwQ?t=2754
|
||||||
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
|
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
|
||||||
- Clean up static_cast when casting to base class.
|
- Clean up static_cast when casting to base class.
|
||||||
|
|||||||
@ -22,8 +22,10 @@ static uint8_t gamma = 255;
|
|||||||
#define ROTATION_270 2
|
#define ROTATION_270 2
|
||||||
#define ROTATION_90 3
|
#define ROTATION_90 3
|
||||||
|
|
||||||
|
|
||||||
static void onBacklightSliderEvent(lv_event_t* event) {
|
static void onBacklightSliderEvent(lv_event_t* event) {
|
||||||
auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event));
|
||||||
|
|
||||||
auto* lvgl_display = lv_display_get_default();
|
auto* lvgl_display = lv_display_get_default();
|
||||||
assert(lvgl_display != nullptr);
|
assert(lvgl_display != nullptr);
|
||||||
auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display);
|
auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display);
|
||||||
@ -55,12 +57,8 @@ static void onGammaSliderEvent(lv_event_t* event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static tt::hal::Display* getHalDisplay(lv_obj_t* widget) {
|
static std::shared_ptr<tt::hal::Display> getHalDisplay() {
|
||||||
auto* lvgl_display = lv_obj_get_display(widget);
|
return hal::findFirstDevice<hal::Display>(hal::Device::Type::Display);
|
||||||
assert(lvgl_display != nullptr);
|
|
||||||
auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display);
|
|
||||||
assert(hal_display != nullptr);
|
|
||||||
return hal_display;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static lv_display_rotation_t orientationSettingToDisplayRotation(uint32_t setting) {
|
static lv_display_rotation_t orientationSettingToDisplayRotation(uint32_t setting) {
|
||||||
@ -103,6 +101,9 @@ class DisplayApp : public App {
|
|||||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||||
|
|
||||||
|
auto hal_display = getHalDisplay();
|
||||||
|
assert(hal_display != nullptr);
|
||||||
|
|
||||||
lvgl::toolbar_create(parent, app);
|
lvgl::toolbar_create(parent, app);
|
||||||
|
|
||||||
lv_obj_t* main_wrapper = lv_obj_create(parent);
|
lv_obj_t* main_wrapper = lv_obj_create(parent);
|
||||||
@ -131,12 +132,9 @@ class DisplayApp : public App {
|
|||||||
lv_obj_t* gamma_slider = lv_slider_create(wrapper);
|
lv_obj_t* gamma_slider = lv_slider_create(wrapper);
|
||||||
lv_obj_set_width(gamma_slider, LV_PCT(50));
|
lv_obj_set_width(gamma_slider, LV_PCT(50));
|
||||||
lv_obj_align(gamma_slider, LV_ALIGN_TOP_RIGHT, -8, 40);
|
lv_obj_align(gamma_slider, LV_ALIGN_TOP_RIGHT, -8, 40);
|
||||||
lv_slider_set_range(gamma_slider, 0, getHalDisplay(parent)->getGammaCurveCount());
|
lv_slider_set_range(gamma_slider, 0, hal_display->getGammaCurveCount());
|
||||||
lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr);
|
lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr);
|
||||||
|
|
||||||
auto* hal_display = getHalDisplay(parent);
|
|
||||||
assert(hal_display != nullptr);
|
|
||||||
|
|
||||||
if (!hal_display->supportsBacklightDuty()) {
|
if (!hal_display->supportsBacklightDuty()) {
|
||||||
lv_slider_set_value(brightness_slider, 255, LV_ANIM_OFF);
|
lv_slider_set_value(brightness_slider, 255, LV_ANIM_OFF);
|
||||||
lv_obj_add_state(brightness_slider, LV_STATE_DISABLED);
|
lv_obj_add_state(brightness_slider, LV_STATE_DISABLED);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "Tactility/lvgl/Toolbar.h"
|
#include "Tactility/lvgl/Toolbar.h"
|
||||||
#include "Tactility/service/loader/Loader.h"
|
#include "Tactility/service/loader/Loader.h"
|
||||||
|
|
||||||
|
#include <Tactility/hal/Device.h>
|
||||||
#include <Tactility/Assets.h>
|
#include <Tactility/Assets.h>
|
||||||
#include <Tactility/Tactility.h>
|
#include <Tactility/Tactility.h>
|
||||||
#include <Tactility/Timer.h>
|
#include <Tactility/Timer.h>
|
||||||
@ -33,7 +34,9 @@ class PowerApp : public App {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
Timer update_timer = Timer(Timer::Type::Periodic, &onTimer, nullptr);
|
Timer update_timer = Timer(Timer::Type::Periodic, &onTimer, nullptr);
|
||||||
std::shared_ptr<tt::hal::Power> power = getConfiguration()->hardware->power();
|
|
||||||
|
std::shared_ptr<hal::Power> power;
|
||||||
|
|
||||||
lv_obj_t* enableLabel = nullptr;
|
lv_obj_t* enableLabel = nullptr;
|
||||||
lv_obj_t* enableSwitch = nullptr;
|
lv_obj_t* enableSwitch = nullptr;
|
||||||
lv_obj_t* batteryVoltageLabel = nullptr;
|
lv_obj_t* batteryVoltageLabel = nullptr;
|
||||||
@ -135,11 +138,19 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
void onCreate(AppContext& app) override {
|
||||||
|
power = hal::findFirstDevice<hal::Power>(hal::Device::Type::Power);
|
||||||
|
}
|
||||||
|
|
||||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||||
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
|
||||||
|
|
||||||
lvgl::toolbar_create(parent, app);
|
lvgl::toolbar_create(parent, app);
|
||||||
|
|
||||||
|
if (power == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
lv_obj_t* wrapper = lv_obj_create(parent);
|
lv_obj_t* wrapper = lv_obj_create(parent);
|
||||||
lv_obj_set_width(wrapper, LV_PCT(100));
|
lv_obj_set_width(wrapper, LV_PCT(100));
|
||||||
lv_obj_set_style_border_width(wrapper, 0, 0);
|
lv_obj_set_style_border_width(wrapper, 0, 0);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "Tactility/lvgl/Toolbar.h"
|
#include "Tactility/lvgl/Toolbar.h"
|
||||||
|
|
||||||
#include <Tactility/Assets.h>
|
#include <Tactility/Assets.h>
|
||||||
|
#include <Tactility/hal/Device.h>
|
||||||
#include <Tactility/Tactility.h>
|
#include <Tactility/Tactility.h>
|
||||||
|
|
||||||
#include <lvgl.h>
|
#include <lvgl.h>
|
||||||
@ -42,17 +43,17 @@ static size_t getSpiTotal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_t total) {
|
static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_t total) {
|
||||||
lv_obj_t* container = lv_obj_create(parent);
|
auto* container = lv_obj_create(parent);
|
||||||
lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
lv_obj_set_style_pad_all(container, 0, 0);
|
lv_obj_set_style_pad_all(container, 0, 0);
|
||||||
lv_obj_set_style_border_width(container, 0, 0);
|
lv_obj_set_style_border_width(container, 0, 0);
|
||||||
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW);
|
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW);
|
||||||
|
|
||||||
lv_obj_t* left_label = lv_label_create(container);
|
auto* left_label = lv_label_create(container);
|
||||||
lv_label_set_text(left_label, label);
|
lv_label_set_text(left_label, label);
|
||||||
lv_obj_set_width(left_label, 60);
|
lv_obj_set_width(left_label, 60);
|
||||||
|
|
||||||
lv_obj_t* bar = lv_bar_create(container);
|
auto* bar = lv_bar_create(container);
|
||||||
lv_obj_set_flex_grow(bar, 1);
|
lv_obj_set_flex_grow(bar, 1);
|
||||||
|
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
@ -63,7 +64,7 @@ static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_
|
|||||||
|
|
||||||
lv_bar_set_value(bar, (int32_t)used, LV_ANIM_OFF);
|
lv_bar_set_value(bar, (int32_t)used, LV_ANIM_OFF);
|
||||||
|
|
||||||
lv_obj_t* bottom_label = lv_label_create(parent);
|
auto* bottom_label = lv_label_create(parent);
|
||||||
lv_label_set_text_fmt(bottom_label, "%u / %u kB", (used / 1024), (total / 1024));
|
lv_label_set_text_fmt(bottom_label, "%u / %u kB", (used / 1024), (total / 1024));
|
||||||
lv_obj_set_width(bottom_label, LV_PCT(100));
|
lv_obj_set_width(bottom_label, LV_PCT(100));
|
||||||
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
|
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
|
||||||
@ -90,19 +91,18 @@ static const char* getTaskState(const TaskStatus_t& task) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void addRtosTask(lv_obj_t* parent, const TaskStatus_t& task) {
|
static void addRtosTask(lv_obj_t* parent, const TaskStatus_t& task) {
|
||||||
lv_obj_t* label = lv_label_create(parent);
|
auto* label = lv_label_create(parent);
|
||||||
const char* name = (task.pcTaskName == nullptr || task.pcTaskName[0] == 0) ? "(unnamed)" : task.pcTaskName;
|
const char* name = (task.pcTaskName == nullptr || task.pcTaskName[0] == 0) ? "(unnamed)" : task.pcTaskName;
|
||||||
lv_label_set_text_fmt(label, "%s (%s)", name, getTaskState(task));
|
lv_label_set_text_fmt(label, "%s (%s)", name, getTaskState(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addRtosTasks(lv_obj_t* parent) {
|
static void addRtosTasks(lv_obj_t* parent) {
|
||||||
UBaseType_t count = uxTaskGetNumberOfTasks();
|
UBaseType_t count = uxTaskGetNumberOfTasks();
|
||||||
TaskStatus_t* tasks = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * count);
|
auto* tasks = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * count);
|
||||||
uint32_t totalRuntime = 0;
|
uint32_t totalRuntime = 0;
|
||||||
UBaseType_t actual = uxTaskGetSystemState(tasks, count, &totalRuntime);
|
UBaseType_t actual = uxTaskGetSystemState(tasks, count, &totalRuntime);
|
||||||
for (int i = 0; i < actual; ++i) {
|
for (int i = 0; i < actual; ++i) {
|
||||||
const TaskStatus_t& task = tasks[i];
|
const TaskStatus_t& task = tasks[i];
|
||||||
TT_LOG_I(TAG, "Task: %s", task.pcTaskName);
|
|
||||||
addRtosTask(parent, task);
|
addRtosTask(parent, task);
|
||||||
}
|
}
|
||||||
free(tasks);
|
free(tasks);
|
||||||
@ -110,6 +110,18 @@ static void addRtosTasks(lv_obj_t* parent) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void addDevice(lv_obj_t* parent, const std::shared_ptr<hal::Device>& device) {
|
||||||
|
auto* label = lv_label_create(parent);
|
||||||
|
lv_label_set_text(label, device->getName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addDevices(lv_obj_t* parent) {
|
||||||
|
auto devices = hal::getDevices();
|
||||||
|
for (const auto& device: devices) {
|
||||||
|
addDevice(parent, device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SystemInfoApp : public App {
|
class SystemInfoApp : public App {
|
||||||
|
|
||||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||||
@ -117,16 +129,16 @@ class SystemInfoApp : public App {
|
|||||||
lvgl::toolbar_create(parent, app);
|
lvgl::toolbar_create(parent, app);
|
||||||
|
|
||||||
// This wrapper automatically has its children added vertically underneath eachother
|
// This wrapper automatically has its children added vertically underneath eachother
|
||||||
lv_obj_t* wrapper = lv_obj_create(parent);
|
auto* wrapper = lv_obj_create(parent);
|
||||||
lv_obj_set_style_border_width(wrapper, 0, 0);
|
lv_obj_set_style_border_width(wrapper, 0, 0);
|
||||||
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_width(wrapper, LV_PCT(100));
|
lv_obj_set_width(wrapper, LV_PCT(100));
|
||||||
lv_obj_set_flex_grow(wrapper, 1);
|
lv_obj_set_flex_grow(wrapper, 1);
|
||||||
|
|
||||||
// Wrapper for the memory usage bars
|
// Wrapper for the memory usage bars
|
||||||
lv_obj_t* memory_label = lv_label_create(wrapper);
|
auto* memory_label = lv_label_create(wrapper);
|
||||||
lv_label_set_text(memory_label, "Memory usage");
|
lv_label_set_text(memory_label, "Memory usage");
|
||||||
lv_obj_t* memory_wrapper = lv_obj_create(wrapper);
|
auto* memory_wrapper = lv_obj_create(wrapper);
|
||||||
lv_obj_set_flex_flow(memory_wrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(memory_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_size(memory_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(memory_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
@ -134,23 +146,29 @@ class SystemInfoApp : public App {
|
|||||||
addMemoryBar(memory_wrapper, "SPI", getSpiTotal() - getSpiFree(), getSpiTotal());
|
addMemoryBar(memory_wrapper, "SPI", getSpiTotal() - getSpiFree(), getSpiTotal());
|
||||||
|
|
||||||
#if configUSE_TRACE_FACILITY
|
#if configUSE_TRACE_FACILITY
|
||||||
lv_obj_t* tasks_label = lv_label_create(wrapper);
|
auto* tasks_label = lv_label_create(wrapper);
|
||||||
lv_label_set_text(tasks_label, "Tasks");
|
lv_label_set_text(tasks_label, "Tasks");
|
||||||
lv_obj_t* tasks_wrapper = lv_obj_create(wrapper);
|
auto* tasks_wrapper = lv_obj_create(wrapper);
|
||||||
lv_obj_set_flex_flow(tasks_wrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(tasks_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_size(tasks_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(tasks_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
addRtosTasks(tasks_wrapper);
|
addRtosTasks(tasks_wrapper);
|
||||||
#endif
|
#endif
|
||||||
|
auto* devices_label = lv_label_create(wrapper);
|
||||||
|
lv_label_set_text(devices_label, "Devices");
|
||||||
|
auto* devices_wrapper = lv_obj_create(wrapper);
|
||||||
|
lv_obj_set_flex_flow(devices_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
|
lv_obj_set_size(devices_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
addDevices(devices_wrapper);
|
||||||
|
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
// Build info
|
// Build info
|
||||||
lv_obj_t* build_info_label = lv_label_create(wrapper);
|
auto* build_info_label = lv_label_create(wrapper);
|
||||||
lv_label_set_text(build_info_label, "Build info");
|
lv_label_set_text(build_info_label, "Build info");
|
||||||
lv_obj_t* build_info_wrapper = lv_obj_create(wrapper);
|
auto* build_info_wrapper = lv_obj_create(wrapper);
|
||||||
lv_obj_set_flex_flow(build_info_wrapper, LV_FLEX_FLOW_COLUMN);
|
lv_obj_set_flex_flow(build_info_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||||
lv_obj_set_size(build_info_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
lv_obj_set_size(build_info_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||||
|
|
||||||
lv_obj_t* esp_idf_version = lv_label_create(build_info_wrapper);
|
auto* esp_idf_version = lv_label_create(build_info_wrapper);
|
||||||
lv_label_set_text_fmt(esp_idf_version, "IDF version: %d.%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
|
lv_label_set_text_fmt(esp_idf_version, "IDF version: %d.%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,14 +11,16 @@
|
|||||||
|
|
||||||
namespace tt::lvgl {
|
namespace tt::lvgl {
|
||||||
|
|
||||||
#define TAG "lvglinit"
|
#define TAG "lvgl_init"
|
||||||
|
|
||||||
bool initDisplay(const hal::Configuration& config) {
|
static std::shared_ptr<tt::hal::Display> initDisplay(const hal::Configuration& config) {
|
||||||
assert(config.createDisplay);
|
assert(config.createDisplay);
|
||||||
auto* display = config.createDisplay();
|
auto display = config.createDisplay();
|
||||||
|
assert(display != nullptr);
|
||||||
|
|
||||||
if (!display->start()) {
|
if (!display->start()) {
|
||||||
TT_LOG_E(TAG, "Display start failed");
|
TT_LOG_E(TAG, "Display start failed");
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_display_t* lvgl_display = display->getLvglDisplay();
|
lv_display_t* lvgl_display = display->getLvglDisplay();
|
||||||
@ -32,17 +34,17 @@ bool initDisplay(const hal::Configuration& config) {
|
|||||||
// esp_lvgl_port users user_data by default, so we have to modify the source
|
// esp_lvgl_port users user_data by default, so we have to modify the source
|
||||||
// this is a check for when we upgrade esp_lvgl_port and forget to modify it again
|
// this is a check for when we upgrade esp_lvgl_port and forget to modify it again
|
||||||
assert(existing_display_user_data == nullptr);
|
assert(existing_display_user_data == nullptr);
|
||||||
lv_display_set_user_data(lvgl_display, display);
|
lv_display_set_user_data(lvgl_display, display.get());
|
||||||
|
|
||||||
lv_display_rotation_t rotation = app::display::getRotation();
|
lv_display_rotation_t rotation = app::display::getRotation();
|
||||||
if (rotation != lv_disp_get_rotation(lv_disp_get_default())) {
|
if (rotation != lv_disp_get_rotation(lv_disp_get_default())) {
|
||||||
lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation));
|
lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initTouch(hal::Display* display, hal::Touch* touch) {
|
static bool initTouch(const std::shared_ptr<hal::Display>& display, const std::shared_ptr<hal::Touch>& touch) {
|
||||||
TT_LOG_I(TAG, "Touch init");
|
TT_LOG_I(TAG, "Touch init");
|
||||||
assert(display);
|
assert(display);
|
||||||
assert(touch);
|
assert(touch);
|
||||||
@ -54,14 +56,14 @@ bool initTouch(hal::Display* display, hal::Touch* touch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool initKeyboard(hal::Display* display, hal::Keyboard* keyboard) {
|
static bool initKeyboard(const std::shared_ptr<hal::Display>& display, const std::shared_ptr<hal::Keyboard>& keyboard) {
|
||||||
TT_LOG_I(TAG, "Keyboard init");
|
TT_LOG_I(TAG, "Keyboard init");
|
||||||
assert(display);
|
assert(display);
|
||||||
assert(keyboard);
|
assert(keyboard);
|
||||||
if (keyboard->isAttached()) {
|
if (keyboard->isAttached()) {
|
||||||
if (keyboard->start(display->getLvglDisplay())) {
|
if (keyboard->start(display->getLvglDisplay())) {
|
||||||
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
|
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
|
||||||
lv_indev_set_user_data(keyboard_indev, keyboard);
|
lv_indev_set_user_data(keyboard_indev, keyboard.get());
|
||||||
tt::lvgl::keypad_set_indev(keyboard_indev);
|
tt::lvgl::keypad_set_indev(keyboard_indev);
|
||||||
TT_LOG_I(TAG, "Keyboard started");
|
TT_LOG_I(TAG, "Keyboard started");
|
||||||
return true;
|
return true;
|
||||||
@ -85,20 +87,24 @@ void init(const hal::Configuration& config) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initDisplay(config)) {
|
auto display = initDisplay(config);
|
||||||
|
if (display == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
hal::registerDevice(display);
|
||||||
|
|
||||||
hal::Display* display = getDisplay();
|
auto touch = display->createTouch();
|
||||||
|
|
||||||
hal::Touch* touch = display->createTouch();
|
|
||||||
if (touch != nullptr) {
|
if (touch != nullptr) {
|
||||||
|
hal::registerDevice(touch);
|
||||||
initTouch(display, touch);
|
initTouch(display, touch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.createKeyboard) {
|
if (config.createKeyboard) {
|
||||||
hal::Keyboard* keyboard = config.createKeyboard();
|
auto keyboard = config.createKeyboard();
|
||||||
initKeyboard(display, keyboard);
|
if (keyboard != nullptr) {
|
||||||
|
hal::registerDevice(keyboard);
|
||||||
|
initKeyboard(display, keyboard);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TT_LOG_I(TAG, "Finished");
|
TT_LOG_I(TAG, "Finished");
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
namespace tt {
|
namespace tt {
|
||||||
|
|
||||||
std::unique_ptr<ScopedLockableUsage> Lockable::scoped() const {
|
std::unique_ptr<ScopedLockableUsage> Lockable::scoped() const {
|
||||||
auto* scoped = new ScopedLockableUsage(*this);
|
return std::make_unique<ScopedLockableUsage>(*this);
|
||||||
return std::unique_ptr<ScopedLockableUsage>(scoped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,8 +12,8 @@ typedef bool (*InitLvgl)();
|
|||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
class Keyboard;
|
class Keyboard;
|
||||||
typedef Display* (*CreateDisplay)();
|
typedef std::shared_ptr<Display> (*CreateDisplay)();
|
||||||
typedef Keyboard* (*CreateKeyboard)();
|
typedef std::shared_ptr<Keyboard> (*CreateKeyboard)();
|
||||||
typedef std::shared_ptr<Power> (*CreatePower)();
|
typedef std::shared_ptr<Power> (*CreatePower)();
|
||||||
|
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
|
|||||||
83
TactilityHeadless/Include/Tactility/hal/Device.h
Normal file
83
TactilityHeadless/Include/Tactility/hal/Device.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace tt::hal {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for HAL-related devices.
|
||||||
|
*/
|
||||||
|
class Device {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class Type {
|
||||||
|
I2c,
|
||||||
|
Display,
|
||||||
|
Touch,
|
||||||
|
SdCard,
|
||||||
|
Keyboard,
|
||||||
|
Power
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef uint32_t Id;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Id id;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Device();
|
||||||
|
virtual ~Device() = default;
|
||||||
|
|
||||||
|
Id getId() const { return id; }
|
||||||
|
|
||||||
|
/** The type of device. */
|
||||||
|
virtual Type getType() const = 0;
|
||||||
|
|
||||||
|
/** The part number or hardware name e.g. TdeckTouch, TdeckDisplay, BQ24295, etc. */
|
||||||
|
virtual std::string getName() const = 0;
|
||||||
|
|
||||||
|
/** A short description of what this device does.
|
||||||
|
* e.g. "USB charging controller with I2C interface."
|
||||||
|
*/
|
||||||
|
virtual std::string getDescription() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a device to the registry.
|
||||||
|
* @warning This will leak memory if you want to destroy a device and don't call deregisterDevice()!
|
||||||
|
*/
|
||||||
|
void registerDevice(const std::shared_ptr<Device>& device);
|
||||||
|
|
||||||
|
/** Remove a device from the registry. */
|
||||||
|
void deregisterDevice(const std::shared_ptr<Device>& device);
|
||||||
|
|
||||||
|
/** Find a device in the registry by its name. */
|
||||||
|
std::shared_ptr<Device> _Nullable findDevice(std::string name);
|
||||||
|
|
||||||
|
/** Find a device in the registry by its identifier. */
|
||||||
|
std::shared_ptr<Device> _Nullable findDevice(Device::Id id);
|
||||||
|
|
||||||
|
/** Find 0, 1 or more devices in the registry by type. */
|
||||||
|
std::vector<std::shared_ptr<Device>> findDevices(Device::Type type);
|
||||||
|
|
||||||
|
/** Get a copy of the entire device registry in its current state. */
|
||||||
|
std::vector<std::shared_ptr<Device>> getDevices();
|
||||||
|
|
||||||
|
template<class DeviceType>
|
||||||
|
std::shared_ptr<DeviceType> findFirstDevice(Device::Type type) {
|
||||||
|
auto devices = findDevices(type);
|
||||||
|
if (devices.empty()) {
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
auto& first = devices[0];
|
||||||
|
return std::static_pointer_cast<DeviceType>(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,13 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "lvgl.h"
|
#include "Device.h"
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
namespace tt::hal {
|
namespace tt::hal {
|
||||||
|
|
||||||
class Touch;
|
class Touch;
|
||||||
|
|
||||||
class Display {
|
class Display : public Device {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
Type getType() const override { return Type::Display; }
|
||||||
|
|
||||||
virtual bool start() = 0;
|
virtual bool start() = 0;
|
||||||
virtual bool stop() = 0;
|
virtual bool stop() = 0;
|
||||||
|
|
||||||
@ -15,7 +21,7 @@ public:
|
|||||||
virtual bool isPoweredOn() const { return true; }
|
virtual bool isPoweredOn() const { return true; }
|
||||||
virtual bool supportsPowerControl() const { return false; }
|
virtual bool supportsPowerControl() const { return false; }
|
||||||
|
|
||||||
virtual Touch* _Nullable createTouch() = 0;
|
virtual std::shared_ptr<Touch> _Nullable createTouch() = 0;
|
||||||
|
|
||||||
/** Set a value in the range [0, 255] */
|
/** Set a value in the range [0, 255] */
|
||||||
virtual void setBacklightDuty(uint8_t backlightDuty) { /* NO-OP */ }
|
virtual void setBacklightDuty(uint8_t backlightDuty) { /* NO-OP */ }
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "lvgl.h"
|
#include "Device.h"
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
namespace tt::hal {
|
namespace tt::hal {
|
||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
|
|
||||||
class Keyboard {
|
class Keyboard : public Device {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
Type getType() const override { return Type::Keyboard; }
|
||||||
|
|
||||||
virtual bool start(lv_display_t* display) = 0;
|
virtual bool start(lv_display_t* display) = 0;
|
||||||
virtual bool stop() = 0;
|
virtual bool stop() = 0;
|
||||||
virtual bool isAttached() const = 0;
|
virtual bool isAttached() const = 0;
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Device.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace tt::hal {
|
namespace tt::hal {
|
||||||
|
|
||||||
class Power{
|
class Power : public Device {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Power() = default;
|
Power() = default;
|
||||||
virtual ~Power() = default;
|
~Power() override = default;
|
||||||
|
|
||||||
|
Type getType() const override { return Type::Power; }
|
||||||
|
|
||||||
enum class MetricType {
|
enum class MetricType {
|
||||||
IsCharging, // bool
|
IsCharging, // bool
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Device.h"
|
||||||
|
|
||||||
#include <Tactility/TactilityCore.h>
|
#include <Tactility/TactilityCore.h>
|
||||||
|
|
||||||
namespace tt::hal {
|
namespace tt::hal {
|
||||||
@ -7,8 +9,10 @@ namespace tt::hal {
|
|||||||
#define TT_SDCARD_MOUNT_NAME "sdcard"
|
#define TT_SDCARD_MOUNT_NAME "sdcard"
|
||||||
#define TT_SDCARD_MOUNT_POINT "/sdcard"
|
#define TT_SDCARD_MOUNT_POINT "/sdcard"
|
||||||
|
|
||||||
class SdCard {
|
class SdCard : public Device {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
Mounted,
|
Mounted,
|
||||||
Unmounted,
|
Unmounted,
|
||||||
@ -22,11 +26,15 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
MountBehaviour mountBehaviour;
|
MountBehaviour mountBehaviour;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit SdCard(MountBehaviour mountBehaviour) : mountBehaviour(mountBehaviour) {}
|
explicit SdCard(MountBehaviour mountBehaviour) : mountBehaviour(mountBehaviour) {}
|
||||||
virtual ~SdCard() = default;
|
virtual ~SdCard() override = default;
|
||||||
|
|
||||||
|
Type getType() const final { return Type::SdCard; };
|
||||||
|
|
||||||
virtual bool mount(const char* mountPath) = 0;
|
virtual bool mount(const char* mountPath) = 0;
|
||||||
virtual bool unmount() = 0;
|
virtual bool unmount() = 0;
|
||||||
|
|||||||
@ -70,6 +70,9 @@ public:
|
|||||||
config(std::move(config))
|
config(std::move(config))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
std::string getName() const final { return "SD Card"; }
|
||||||
|
std::string getDescription() const final { return "SD card via SPI interface"; }
|
||||||
|
|
||||||
bool mount(const char* mountPath) override;
|
bool mount(const char* mountPath) override;
|
||||||
bool unmount() override;
|
bool unmount() override;
|
||||||
State getState() const override;
|
State getState() const override;
|
||||||
|
|||||||
@ -1,14 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "lvgl.h"
|
#include "Device.h"
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
namespace tt::hal {
|
namespace tt::hal {
|
||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
|
|
||||||
class Touch {
|
class Touch : public Device {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
Type getType() const override { return Type::SdCard; }
|
||||||
|
|
||||||
virtual bool start(lv_display_t* display) = 0;
|
virtual bool start(lv_display_t* display) = 0;
|
||||||
virtual bool stop() = 0;
|
virtual bool stop() = 0;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "./I2c.h"
|
#include "I2c.h"
|
||||||
|
#include "../Device.h"
|
||||||
|
|
||||||
|
namespace tt::hal::i2c {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an I2C peripheral at a specific port and address.
|
* Represents an I2C peripheral at a specific port and address.
|
||||||
@ -8,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
* All read and write calls are thread-safe.
|
* All read and write calls are thread-safe.
|
||||||
*/
|
*/
|
||||||
class I2cDevice {
|
class I2cDevice : public Device {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -17,6 +20,8 @@ protected:
|
|||||||
|
|
||||||
static constexpr TickType_t DEFAULT_TIMEOUT = 1000 / portTICK_PERIOD_MS;
|
static constexpr TickType_t DEFAULT_TIMEOUT = 1000 / portTICK_PERIOD_MS;
|
||||||
|
|
||||||
|
Type getType() const override { return Type::I2c; }
|
||||||
|
|
||||||
bool readRegister8(uint8_t reg, uint8_t& result) const;
|
bool readRegister8(uint8_t reg, uint8_t& result) const;
|
||||||
bool writeRegister8(uint8_t reg, uint8_t value) const;
|
bool writeRegister8(uint8_t reg, uint8_t value) const;
|
||||||
bool readRegister12(uint8_t reg, float& out) const;
|
bool readRegister12(uint8_t reg, float& out) const;
|
||||||
@ -26,7 +31,10 @@ protected:
|
|||||||
bool bitOff(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 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); }
|
bool bitOffByIndex(uint8_t reg, uint8_t index) const { return bitOff(reg, 1 << index); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
explicit I2cDevice(i2c_port_t port, uint32_t address) : port(port), address(address) {}
|
explicit I2cDevice(i2c_port_t port, uint32_t address) : port(port), address(address) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
97
TactilityHeadless/Source/hal/Device.cpp
Normal file
97
TactilityHeadless/Source/hal/Device.cpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#include "Tactility/hal/Device.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
#include <Tactility/Mutex.h>
|
||||||
|
|
||||||
|
namespace tt::hal {
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> devices;
|
||||||
|
Mutex mutex = Mutex(Mutex::Type::Recursive);
|
||||||
|
static Device::Id nextId = 0;
|
||||||
|
|
||||||
|
#define TAG "devices"
|
||||||
|
|
||||||
|
Device::Device() : id(nextId++) {}
|
||||||
|
|
||||||
|
template <std::ranges::range RangeType>
|
||||||
|
auto toVector(RangeType&& range) {
|
||||||
|
auto view = range | std::views::common;
|
||||||
|
return std::vector(view.begin(), view.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerDevice(const std::shared_ptr<Device>& device) {
|
||||||
|
auto scoped_mutex = mutex.scoped();
|
||||||
|
if (scoped_mutex->lock()) {
|
||||||
|
if (findDevice(device->getId()) == nullptr) {
|
||||||
|
devices.push_back(device);
|
||||||
|
TT_LOG_I(TAG, "Registered %s with id %lu", device->getName().c_str(), device->getId());
|
||||||
|
} else {
|
||||||
|
TT_LOG_W(TAG, "Device %s with id %lu was already registered", device->getName().c_str(), device->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void deregisterDevice(const std::shared_ptr<Device>& device) {
|
||||||
|
auto scoped_mutex = mutex.scoped();
|
||||||
|
auto id_to_remove = device->getId();
|
||||||
|
if (scoped_mutex->lock()) {
|
||||||
|
auto remove_iterator = std::remove_if(devices.begin(), devices.end(), [id_to_remove](const auto& device) {
|
||||||
|
return device->getId() == id_to_remove;
|
||||||
|
});
|
||||||
|
if (remove_iterator != devices.end()) {
|
||||||
|
TT_LOG_I(TAG, "Deregistering %s with id %lu", device->getName().c_str(), device->getId());
|
||||||
|
devices.erase(remove_iterator);
|
||||||
|
} else {
|
||||||
|
TT_LOG_W(TAG, "Deregistering %s with id %lu failed: not found", device->getName().c_str(), device->getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> findDevices(const std::function<bool(const std::shared_ptr<Device>&)>& filterFunction) {
|
||||||
|
auto scoped_mutex = mutex.scoped();
|
||||||
|
if (scoped_mutex->lock()) {
|
||||||
|
auto devices_view = devices | std::views::filter([&filterFunction](auto& device) {
|
||||||
|
return filterFunction(device);
|
||||||
|
});
|
||||||
|
return toVector(devices_view);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<Device> _Nullable findDevice(const std::function<bool(const std::shared_ptr<Device>&)>& filterFunction) {
|
||||||
|
auto scoped_mutex = mutex.scoped();
|
||||||
|
if (scoped_mutex->lock()) {
|
||||||
|
auto result_set = devices | std::views::filter([&filterFunction](auto& device) {
|
||||||
|
return filterFunction(device);
|
||||||
|
});
|
||||||
|
if (!result_set.empty()) {
|
||||||
|
return result_set.front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Device> _Nullable findDevice(std::string name) {
|
||||||
|
return findDevice([&name](auto& device){
|
||||||
|
return device->getName() == name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Device> _Nullable findDevice(Device::Id id) {
|
||||||
|
return findDevice([id](auto& device){
|
||||||
|
return device->getId() == id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> findDevices(Device::Type type) {
|
||||||
|
return findDevices([type](auto& device) {
|
||||||
|
return device->getType() == type;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Device>> getDevices() {
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include "Tactility/hal/Device.h"
|
||||||
#include "Tactility/hal/Hal_i.h"
|
#include "Tactility/hal/Hal_i.h"
|
||||||
#include "Tactility/hal/i2c/I2c.h"
|
#include "Tactility/hal/i2c/I2c.h"
|
||||||
|
|
||||||
@ -28,6 +29,12 @@ void init(const Configuration& configuration) {
|
|||||||
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT)) {
|
if (!configuration.sdcard->mount(TT_SDCARD_MOUNT_POINT)) {
|
||||||
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
|
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
|
||||||
}
|
}
|
||||||
|
hal::registerDevice(configuration.sdcard);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configuration.power != nullptr) {
|
||||||
|
std::shared_ptr<tt::hal::Power> power = configuration.power();
|
||||||
|
hal::registerDevice(power);
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel::systemEventPublish(kernel::SystemEvent::BootInitHalEnd);
|
kernel::systemEventPublish(kernel::SystemEvent::BootInitHalEnd);
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace tt::hal::i2c {
|
||||||
|
|
||||||
bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
|
bool I2cDevice::readRegister12(uint8_t reg, float& out) const {
|
||||||
std::uint8_t data[2] = {0};
|
std::uint8_t data[2] = {0};
|
||||||
if (tt::hal::i2c::masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
if (tt::hal::i2c::masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) {
|
||||||
@ -59,3 +61,5 @@ bool I2cDevice::bitOff(uint8_t reg, uint8_t bitmask) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|||||||
@ -4,6 +4,8 @@ set(DOCTESTINC ${PROJECT_SOURCE_DIR}/Include)
|
|||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(TactilityCore)
|
add_subdirectory(TactilityCore)
|
||||||
|
add_subdirectory(TactilityHeadless)
|
||||||
|
|
||||||
add_custom_target(build-tests)
|
add_custom_target(build-tests)
|
||||||
add_dependencies(build-tests TactilityCoreTests)
|
add_dependencies(build-tests TactilityCoreTests)
|
||||||
|
add_dependencies(build-tests TactilityHeadlessTests)
|
||||||
|
|||||||
27
Tests/TactilityHeadless/CMakeLists.txt
Normal file
27
Tests/TactilityHeadless/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
project(TactilityCoreTests)
|
||||||
|
|
||||||
|
enable_language(C CXX ASM)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_COMPILER g++)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp)
|
||||||
|
add_executable(TactilityHeadlessTests EXCLUDE_FROM_ALL ${TEST_SOURCES})
|
||||||
|
|
||||||
|
add_definitions(-D_Nullable=)
|
||||||
|
add_definitions(-D_Nonnull=)
|
||||||
|
|
||||||
|
target_include_directories(TactilityHeadlessTests PRIVATE
|
||||||
|
${DOCTESTINC}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME TactilityHeadlessTests
|
||||||
|
COMMAND TactilityHeadlessTests
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(TactilityHeadlessTests PRIVATE
|
||||||
|
Tactility
|
||||||
|
TactilityCore
|
||||||
|
TactilityHeadless
|
||||||
|
Simulator
|
||||||
|
SDL2::SDL2-static SDL2-static
|
||||||
|
)
|
||||||
98
Tests/TactilityHeadless/HalDeviceTest.cpp
Normal file
98
Tests/TactilityHeadless/HalDeviceTest.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include "doctest.h"
|
||||||
|
#include <Tactility/hal/Device.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace tt;
|
||||||
|
|
||||||
|
class TestDevice final : public hal::Device {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
hal::Device::Type type;
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
TestDevice(hal::Device::Type type, std::string name, std::string description) :
|
||||||
|
type(type),
|
||||||
|
name(std::move(name)),
|
||||||
|
description(std::move(description))
|
||||||
|
{}
|
||||||
|
|
||||||
|
TestDevice() : TestDevice(hal::Device::Type::Power, "PowerMock", "PowerMock description") {}
|
||||||
|
|
||||||
|
~TestDevice() final = default;
|
||||||
|
|
||||||
|
Type getType() const final { return type; }
|
||||||
|
std::string getName() const final { return name; }
|
||||||
|
std::string getDescription() const final { return description; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeviceAutoRegistration {
|
||||||
|
|
||||||
|
std::shared_ptr<hal::Device> device;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit DeviceAutoRegistration(std::shared_ptr<hal::Device> inDevice) : device(std::move(inDevice)) {
|
||||||
|
hal::registerDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DeviceAutoRegistration() {
|
||||||
|
hal::deregisterDevice(device);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** We add 3 tests into 1 to ensure cleanup happens */
|
||||||
|
TEST_CASE("registering and deregistering a device works") {
|
||||||
|
auto device = std::make_shared<TestDevice>();
|
||||||
|
|
||||||
|
// Pre-registration
|
||||||
|
CHECK_EQ(hal::findDevice(device->getId()), nullptr);
|
||||||
|
|
||||||
|
// Registration
|
||||||
|
hal::registerDevice(device);
|
||||||
|
auto found_device = hal::findDevice(device->getId());
|
||||||
|
CHECK_NE(found_device, nullptr);
|
||||||
|
CHECK_EQ(found_device->getId(), device->getId());
|
||||||
|
|
||||||
|
// Deregistration
|
||||||
|
hal::deregisterDevice(device);
|
||||||
|
CHECK_EQ(hal::findDevice(device->getId()), nullptr);
|
||||||
|
found_device = nullptr; // to decrease use count
|
||||||
|
CHECK_EQ(device.use_count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("find device by id") {
|
||||||
|
auto device = std::make_shared<TestDevice>();
|
||||||
|
DeviceAutoRegistration auto_registration(device);
|
||||||
|
|
||||||
|
auto found_device = hal::findDevice(device->getId());
|
||||||
|
CHECK_NE(found_device, nullptr);
|
||||||
|
CHECK_EQ(found_device->getId(), device->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("find device by name") {
|
||||||
|
auto device = std::make_shared<TestDevice>();
|
||||||
|
DeviceAutoRegistration auto_registration(device);
|
||||||
|
|
||||||
|
auto found_device = hal::findDevice(device->getName());
|
||||||
|
CHECK_NE(found_device, nullptr);
|
||||||
|
CHECK_EQ(found_device->getId(), device->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("find device by type") {
|
||||||
|
// Headless mode shouldn't have a display, so we want to create one to find only our own display as unique device
|
||||||
|
// We first verify the initial assumption that there is no display:
|
||||||
|
auto unexpected_display = hal::findFirstDevice<TestDevice>(hal::Device::Type::Display);
|
||||||
|
CHECK_EQ(unexpected_display, nullptr);
|
||||||
|
|
||||||
|
auto device = std::make_shared<TestDevice>(hal::Device::Type::Display, "DisplayMock", "");
|
||||||
|
DeviceAutoRegistration auto_registration(device);
|
||||||
|
|
||||||
|
auto found_device = hal::findFirstDevice<TestDevice>(hal::Device::Type::Display);
|
||||||
|
CHECK_NE(found_device, nullptr);
|
||||||
|
CHECK_EQ(found_device->getId(), device->getId());
|
||||||
|
}
|
||||||
51
Tests/TactilityHeadless/Main.cpp
Normal file
51
Tests/TactilityHeadless/Main.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#define DOCTEST_CONFIG_IMPLEMENT
|
||||||
|
#include "doctest.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int argc;
|
||||||
|
char** argv;
|
||||||
|
int result;
|
||||||
|
} TestTaskData;
|
||||||
|
|
||||||
|
void test_task(void* parameter) {
|
||||||
|
auto* data = (TestTaskData*)parameter;
|
||||||
|
|
||||||
|
doctest::Context context;
|
||||||
|
|
||||||
|
context.applyCommandLine(data->argc, data->argv);
|
||||||
|
|
||||||
|
// overrides
|
||||||
|
context.setOption("no-breaks", true); // don't break in the debugger when assertions fail
|
||||||
|
|
||||||
|
data->result = context.run();
|
||||||
|
|
||||||
|
if (context.shouldExit()) { // important - query flags (and --exit) rely on the user doing this
|
||||||
|
vTaskEndScheduler();
|
||||||
|
}
|
||||||
|
|
||||||
|
vTaskDelete(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
TestTaskData data = {
|
||||||
|
.argc = argc,
|
||||||
|
.argv = argv,
|
||||||
|
.result = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
BaseType_t task_result = xTaskCreate(
|
||||||
|
test_task,
|
||||||
|
"test_task",
|
||||||
|
8192,
|
||||||
|
&data,
|
||||||
|
1,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
assert(task_result == pdPASS);
|
||||||
|
|
||||||
|
vTaskStartScheduler();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user