Compare commits

..

4 Commits

Author SHA1 Message Date
Ken Van Hoeylandt
15613ae0ff Formatting 2025-08-30 19:59:40 +02:00
Ken Van Hoeylandt
0c50c03c12 Various improvements 2025-08-30 19:33:00 +02:00
Ken Van Hoeylandt
28026d817f Fixes for power 2025-08-30 17:38:22 +02:00
Ken Van Hoeylandt
c087d997f3 Auto-buzz on startup 2025-08-30 13:02:59 +02:00
17 changed files with 174 additions and 73 deletions

View File

@ -1,3 +1,4 @@
#include <Bq27220.h>
#include <Tactility/TactilityCore.h> #include <Tactility/TactilityCore.h>
#include <Tactility/kernel/SystemEvents.h> #include <Tactility/kernel/SystemEvents.h>
#include <Tactility/service/gps/GpsService.h> #include <Tactility/service/gps/GpsService.h>
@ -21,6 +22,18 @@ bool tpagerInit() {
} }
tt::kernel::subscribeSystemEvent(tt::kernel::SystemEvent::BootSplash, [](auto) { tt::kernel::subscribeSystemEvent(tt::kernel::SystemEvent::BootSplash, [](auto) {
tt::hal::findDevices([](auto device) {
if (device->getName() == "BQ27220") {
auto bq27220 = std::reinterpret_pointer_cast<Bq27220>(device);
if (bq27220 != nullptr) {
bq27220->configureCapacity(1500, 1500);
return false;
}
}
return true;
});
auto gps_service = tt::service::gps::findGpsService(); auto gps_service = tt::service::gps::findGpsService();
if (gps_service != nullptr) { if (gps_service != nullptr) {
std::vector<tt::hal::gps::GpsConfiguration> gps_configurations; std::vector<tt::hal::gps::GpsConfiguration> gps_configurations;

View File

@ -18,7 +18,6 @@ using namespace tt::hal;
DeviceVector createDevices() { DeviceVector createDevices() {
auto bq27220 = std::make_shared<Bq27220>(I2C_NUM_0); auto bq27220 = std::make_shared<Bq27220>(I2C_NUM_0);
bq27220->configureCapacity(1500, 1500);
auto power = std::make_shared<TpagerPower>(bq27220); auto power = std::make_shared<TpagerPower>(bq27220);
auto tca8418 = std::make_shared<Tca8418>(I2C_NUM_0); auto tca8418 = std::make_shared<Tca8418>(I2C_NUM_0);
@ -42,10 +41,10 @@ extern const Configuration lilygo_tlora_pager = {
.createDevices = createDevices, .createDevices = createDevices,
.i2c = { .i2c = {
i2c::Configuration { i2c::Configuration {
.name = "Shared", .name = "Internal",
.port = I2C_NUM_0, .port = I2C_NUM_0,
.initMode = i2c::InitMode::ByTactility, .initMode = i2c::InitMode::ByTactility,
.isMutable = true, .isMutable = false,
.config = (i2c_config_t) { .config = (i2c_config_t) {
.mode = I2C_MODE_MASTER, .mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_3, .sda_io_num = GPIO_NUM_3,

View File

@ -43,7 +43,6 @@ void TpagerKeyboard::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
char keypress = 0; char keypress = 0;
if (xQueueReceive(keyboard->queue, &keypress, pdMS_TO_TICKS(50)) == pdPASS) { if (xQueueReceive(keyboard->queue, &keypress, pdMS_TO_TICKS(50)) == pdPASS) {
TT_LOG_I(TAG, "Keypress: %c", keypress);
data->key = keypress; data->key = keypress;
data->state = LV_INDEV_STATE_PRESSED; data->state = LV_INDEV_STATE_PRESSED;
} else { } else {

View File

@ -16,7 +16,7 @@
## Lower Priority ## Lower Priority
- Support hot-plugging SD card - Support hot-plugging SD card (note: this is not possible if they require the CS pin hack)
- Create more unit tests for `tactility` - Create more unit tests for `tactility`
- Explore LVGL9's FreeRTOS functionality - Explore LVGL9's FreeRTOS functionality
- CrashHandler: use "corrupted" flag - CrashHandler: use "corrupted" flag

View File

@ -127,7 +127,7 @@ bool Bq27220::getCurrent(int16_t &value) {
return false; return false;
} }
bool Bq27220::getBatteryStatus(Bq27220::BatteryStatus &batt_sta) { bool Bq27220::getBatteryStatus(BatteryStatus &batt_sta) {
if (readRegister16(registers::CMD_BATTERY_STATUS, batt_sta.full)) { if (readRegister16(registers::CMD_BATTERY_STATUS, batt_sta.full)) {
swapEndianess(batt_sta.full); swapEndianess(batt_sta.full);
return true; return true;

View File

@ -18,15 +18,8 @@ bool Drv2605::init() {
writeRegister(Register::RealtimePlaybackInput, 0x00); // Disable writeRegister(Register::RealtimePlaybackInput, 0x00); // Disable
writeRegister(Register::WaveSequence1, 1); // Strong click
writeRegister(Register::WaveSequence2, 0); // End sequence
writeRegister(Register::OverdriveTimeOffset, 0); // No overdrive setWaveFormForClick();
writeRegister(Register::SustainTimeOffsetPostivie, 0);
writeRegister(Register::SustainTimeOffsetNegative, 0);
writeRegister(Register::BrakeTimeOffset, 0);
writeRegister(Register::AudioInputLevelMax, 0x64);
// ERM open loop // ERM open loop
@ -43,6 +36,34 @@ bool Drv2605::init() {
return true; return true;
} }
void Drv2605::setWaveFormForBuzz() {
writeRegister(Register::WaveSequence1, 1); // Strong click
writeRegister(Register::WaveSequence2, 1); // Strong click
writeRegister(Register::WaveSequence3, 1); // Strong click
writeRegister(Register::WaveSequence4, 0); // End sequence
writeRegister(Register::OverdriveTimeOffset, 0); // No overdrive
writeRegister(Register::SustainTimeOffsetPostivie, 0);
writeRegister(Register::SustainTimeOffsetNegative, 0);
writeRegister(Register::BrakeTimeOffset, 0);
writeRegister(Register::AudioInputLevelMax, 0x64);
}
void Drv2605::setWaveFormForClick() {
writeRegister(Register::WaveSequence1, 1); // Strong click
writeRegister(Register::WaveSequence2, 0); // End sequence
writeRegister(Register::OverdriveTimeOffset, 0); // No overdrive
writeRegister(Register::SustainTimeOffsetPostivie, 0);
writeRegister(Register::SustainTimeOffsetNegative, 0);
writeRegister(Register::BrakeTimeOffset, 0);
writeRegister(Register::AudioInputLevelMax, 0x64);
}
void Drv2605::setWaveForm(uint8_t slot, uint8_t waveform) { void Drv2605::setWaveForm(uint8_t slot, uint8_t waveform) {
writeRegister8(static_cast<uint8_t>(Register::WaveSequence1) + slot, waveform); writeRegister8(static_cast<uint8_t>(Register::WaveSequence1) + slot, waveform);
} }

View File

@ -7,6 +7,8 @@ class Drv2605 : public tt::hal::i2c::I2cDevice {
static constexpr auto* TAG = "DRV2605"; static constexpr auto* TAG = "DRV2605";
static constexpr auto ADDRESS = 0x5A; static constexpr auto ADDRESS = 0x5A;
bool autoPlayStartupBuzz;
// Chip IDs // Chip IDs
enum class ChipId { enum class ChipId {
DRV2604 = 0x04, // Has RAM. Doesn't havew licensed ROM library. DRV2604 = 0x04, // Has RAM. Doesn't havew licensed ROM library.
@ -61,10 +63,15 @@ class Drv2605 : public tt::hal::i2c::I2cDevice {
public: public:
explicit Drv2605(i2c_port_t port) : I2cDevice(port, ADDRESS) { explicit Drv2605(i2c_port_t port, bool autoPlayStartupBuzz = true) : I2cDevice(port, ADDRESS), autoPlayStartupBuzz(autoPlayStartupBuzz) {
if (!init()) { if (!init()) {
TT_LOG_E(TAG, "Failed to initialize DRV2605"); TT_LOG_E(TAG, "Failed to initialize DRV2605");
} }
if (autoPlayStartupBuzz) {
setWaveFormForBuzz();
startPlayback();
}
} }
std::string getName() const final { return "DRV2605"; } std::string getName() const final { return "DRV2605"; }
@ -72,6 +79,14 @@ public:
bool init(); bool init();
void setWaveFormForBuzz();
void setWaveFormForClick();
/**
*
* @param slot a value from 0 to 7
* @param waveform
*/
void setWaveForm(uint8_t slot, uint8_t waveform); void setWaveForm(uint8_t slot, uint8_t waveform);
void selectLibrary(uint8_t library); void selectLibrary(uint8_t library);
void startPlayback(); void startPlayback();

View File

@ -133,7 +133,14 @@ lvgl_port_display_cfg_t St7796Display::getLvglPortDisplayConfig(esp_lcd_panel_io
.mirror_y = configuration->mirrorY, .mirror_y = configuration->mirrorY,
}, },
.color_format = LV_COLOR_FORMAT_NATIVE, .color_format = LV_COLOR_FORMAT_NATIVE,
.flags = {.buff_dma = true, .buff_spiram = false, .sw_rotate = false, .swap_bytes = true, .full_refresh = false, .direct_mode = false} .flags = {
.buff_dma = true,
.buff_spiram = false,
.sw_rotate = false,
.swap_bytes = true,
.full_refresh = false,
.direct_mode = false
}
}; };
} }

View File

@ -0,0 +1,7 @@
#pragma once
namespace tt::hal::sdcard {
void mountAll();
}

View File

@ -114,7 +114,7 @@ static void registerSystemApps() {
addApp(app::development::manifest); addApp(app::development::manifest);
#endif #endif
if (getConfiguration()->hardware->power != nullptr) { if (hal::findDevices(hal::Device::Type::Power).size() > 0) {
addApp(app::power::manifest); addApp(app::power::manifest);
} }
} }

View File

@ -6,6 +6,7 @@
#include "Tactility/service/ServiceRegistration.h" #include "Tactility/service/ServiceRegistration.h"
#include <Tactility/Dispatcher.h> #include <Tactility/Dispatcher.h>
#include <Tactility/hal/sdcard/SdCardMounting.h>
#include <Tactility/settings/TimePrivate.h> #include <Tactility/settings/TimePrivate.h>
#ifdef ESP_PLATFORM #ifdef ESP_PLATFORM
@ -14,7 +15,7 @@
namespace tt { namespace tt {
#define TAG "tactility" constexpr auto* TAG = "Tactility";
namespace service::gps { extern const ServiceManifest manifest; } namespace service::gps { extern const ServiceManifest manifest; }
namespace service::wifi { extern const ServiceManifest manifest; } namespace service::wifi { extern const ServiceManifest manifest; }
@ -47,11 +48,11 @@ void initHeadless(const hal::Configuration& config) {
hardwareConfig = &config; hardwareConfig = &config;
settings::initTimeZone(); settings::initTimeZone();
hal::init(config); hal::init(config);
hal::sdcard::mountAll();
network::ntp::init(); network::ntp::init();
registerAndStartSystemServices(); registerAndStartSystemServices();
} }
Dispatcher& getMainDispatcher() { Dispatcher& getMainDispatcher() {
return mainDispatcher; return mainDispatcher;
} }

View File

@ -22,10 +22,10 @@
#define CONFIG_TT_SPLASH_DURATION 0 #define CONFIG_TT_SPLASH_DURATION 0
#endif #endif
#define TAG "boot"
namespace tt::app::boot { namespace tt::app::boot {
constexpr auto* TAG = "Boot";
static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() { static std::shared_ptr<hal::display::DisplayDevice> getHalDisplay() {
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display); return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
} }
@ -60,6 +60,7 @@ class BootApp : public App {
} }
} }
static bool setupUsbBootMode() { static bool setupUsbBootMode() {
if (!hal::usb::isUsbBootMode()) { if (!hal::usb::isUsbBootMode()) {
return false; return false;
@ -86,7 +87,7 @@ class BootApp : public App {
kernel::publishSystemEvent(kernel::SystemEvent::BootSplash); kernel::publishSystemEvent(kernel::SystemEvent::BootSplash);
setupDisplay(); setupDisplay(); // Set backlight
if (!setupUsbBootMode()) { if (!setupUsbBootMode()) {
initFromBootApp(); initFromBootApp();

View File

@ -8,13 +8,13 @@
#include <Tactility/kernel/SystemEvents.h> #include <Tactility/kernel/SystemEvents.h>
#define TAG "hal"
#define TT_SDCARD_MOUNT_POINT "/sdcard"
namespace tt::hal { namespace tt::hal {
void initDevices(const Configuration& configuration) { constexpr auto* TAG = "hal";
void registerDevices(const Configuration& configuration) {
TT_LOG_I(TAG, "Registering devices");
if (configuration.sdcard != nullptr) { if (configuration.sdcard != nullptr) {
registerDevice(configuration.sdcard); registerDevice(configuration.sdcard);
} }
@ -35,29 +35,6 @@ void initDevices(const Configuration& configuration) {
for (auto& device : devices) { for (auto& device : devices) {
registerDevice(device); registerDevice(device);
} }
// TODO: Move
auto sdcards = hal::findDevices<sdcard::SdCardDevice>(Device::Type::SdCard);
if (!sdcards.empty()) {
if (sdcards.size() == 1) {
// Fixed mount path name
auto sdcard = sdcards[0];
TT_LOG_I(TAG, "Mounting sdcard at %s", TT_SDCARD_MOUNT_POINT);
if (!sdcard->mount(TT_SDCARD_MOUNT_POINT)) {
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
}
} else {
// Numbered mount path name
for (int i = 0; i < sdcards.size(); i++) {
auto sdcard = sdcards[i];
std::string mount_path = TT_SDCARD_MOUNT_POINT + std::to_string(i);
TT_LOG_I(TAG, "Mounting sdcard at %d", mount_path.c_str());
if (!sdcard->mount(mount_path)) {
TT_LOG_W(TAG, "SD card mount failed (init can continue)");
}
}
}
}
} }
void init(const Configuration& configuration) { void init(const Configuration& configuration) {
@ -80,7 +57,7 @@ void init(const Configuration& configuration) {
tt_check(configuration.initBoot(), "Init power failed"); tt_check(configuration.initBoot(), "Init power failed");
} }
initDevices(configuration); registerDevices(configuration);
kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd); kernel::publishSystemEvent(kernel::SystemEvent::BootInitHalEnd);
} }

View File

@ -0,0 +1,40 @@
#include <Tactility/hal/sdcard/SdCardMounting.h>
#include <Tactility/hal/sdcard/SdCardDevice.h>
namespace tt::hal::sdcard {
constexpr auto* TAG = "SdCardMounting";
constexpr auto* TT_SDCARD_MOUNT_POINT = "/sdcard";
static void mount(const std::shared_ptr<SdCardDevice>& sdcard, const std::string& path) {
sdcard->getLock()->withLock([&sdcard, &path] {
TT_LOG_I(TAG, "Mounting sdcard at %s", path.c_str());
if (!sdcard->mount(path)) {
TT_LOG_W(TAG, "SD card mount failed for %s (init can continue)", path.c_str());
}
});
}
void mountAll() {
auto sdcards = hal::findDevices<SdCardDevice>(Device::Type::SdCard);
if (!sdcards.empty()) {
if (sdcards.size() == 1) {
// Fixed mount path name
auto sdcard = sdcards[0];
if (!sdcard->isMounted()) {
mount(sdcard, TT_SDCARD_MOUNT_POINT);
}
} else {
// Numbered mount path name
for (int i = 0; i < sdcards.size(); i++) {
auto sdcard = sdcards[i];
if (!sdcard->isMounted()) {
std::string mount_path = TT_SDCARD_MOUNT_POINT + std::to_string(i);
mount(sdcard, mount_path);
}
}
}
}
}
}

View File

@ -21,30 +21,29 @@ static Mode currentMode = Mode::Default;
static RTC_NOINIT_ATTR BootMode bootMode; static RTC_NOINIT_ATTR BootMode bootMode;
sdmmc_card_t* _Nullable getCard() { sdmmc_card_t* _Nullable getCard() {
auto sdcard = getConfiguration()->sdcard; auto sdcards = findDevices<sdcard::SpiSdCardDevice>(Device::Type::SdCard);
if (sdcard == nullptr) {
TT_LOG_W(TAG, "No SD card configuration found"); std::shared_ptr<sdcard::SpiSdCardDevice> usable_sdcard;
for (auto& sdcard : sdcards) {
auto sdcard_candidate = std::static_pointer_cast<sdcard::SpiSdCardDevice>(sdcard);
if (sdcard_candidate != nullptr && sdcard_candidate->isMounted() && sdcard_candidate->getCard() != nullptr) {
usable_sdcard = sdcard_candidate;
break;
}
}
if (usable_sdcard == nullptr) {
TT_LOG_W(TAG, "Couldn't find a mounted SpiSdCard");
return nullptr; return nullptr;
} }
if (!sdcard->isMounted()) { auto* sdmmc_card = usable_sdcard->getCard();
TT_LOG_W(TAG, "SD card not mounted"); if (sdmmc_card == nullptr) {
return nullptr;
}
auto spi_sdcard = std::static_pointer_cast<sdcard::SpiSdCardDevice>(sdcard);
if (spi_sdcard == nullptr) {
TT_LOG_W(TAG, "SD card interface is not supported (must be SpiSdCard)");
return nullptr;
}
auto* card = spi_sdcard->getCard();
if (card == nullptr) {
TT_LOG_W(TAG, "SD card has no card object available"); TT_LOG_W(TAG, "SD card has no card object available");
return nullptr; return nullptr;
} }
return card; return sdmmc_card;
} }
static bool canStartNewMode() { static bool canStartNewMode() {

View File

@ -178,7 +178,7 @@ void start() {
} }
void stop() { void stop() {
TT_LOG_I(TAG, "Stop LVGL"); TT_LOG_I(TAG, "Stopping LVGL");
if (!started) { if (!started) {
TT_LOG_W(TAG, "Can't stop LVGL: not started"); TT_LOG_W(TAG, "Can't stop LVGL: not started");
@ -195,6 +195,7 @@ void stop() {
// Stop keyboards // Stop keyboards
TT_LOG_I(TAG, "Stopping keyboards");
auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard); auto keyboards = hal::findDevices<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
for (auto keyboard : keyboards) { for (auto keyboard : keyboards) {
if (keyboard->getLvglIndev() != nullptr) { if (keyboard->getLvglIndev() != nullptr) {
@ -204,6 +205,7 @@ void stop() {
// Stop touch // Stop touch
TT_LOG_I(TAG, "Stopping touch");
// The display generally stops their own touch devices, but we'll clean up anything that didn't // The display generally stops their own touch devices, but we'll clean up anything that didn't
auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch); auto touch_devices = hal::findDevices<hal::touch::TouchDevice>(hal::Device::Type::Touch);
for (auto touch_device : touch_devices) { for (auto touch_device : touch_devices) {
@ -212,8 +214,19 @@ void stop() {
} }
} }
// Stop encoders
TT_LOG_I(TAG, "Stopping encoders");
// The display generally stops their own touch devices, but we'll clean up anything that didn't
auto encoder_devices = hal::findDevices<hal::encoder::EncoderDevice>(hal::Device::Type::Encoder);
for (auto encoder_device : encoder_devices) {
if (encoder_device->getLvglIndev() != nullptr) {
encoder_device->stopLvgl();
}
}
// Stop displays (and their touch devices) // Stop displays (and their touch devices)
TT_LOG_I(TAG, "Stopping displays");
auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display); auto displays = hal::findDevices<hal::display::DisplayDevice>(hal::Device::Type::Display);
for (auto display : displays) { for (auto display : displays) {
if (display->supportsLvgl() && display->getLvglDisplay() != nullptr && !display->stopLvgl()) { if (display->supportsLvgl() && display->getLvglDisplay() != nullptr && !display->stopLvgl()) {
@ -224,6 +237,8 @@ void stop() {
started = false; started = false;
kernel::publishSystemEvent(kernel::SystemEvent::LvglStopped); kernel::publishSystemEvent(kernel::SystemEvent::LvglStopped);
TT_LOG_I(TAG, "Stopped LVGL");
} }
} // namespace } // namespace

View File

@ -89,13 +89,20 @@ static const char* getSdCardStatusIcon(hal::sdcard::SdCardDevice::State state) {
} }
static _Nullable const char* getPowerStatusIcon() { static _Nullable const char* getPowerStatusIcon() {
auto get_power = getConfiguration()->hardware->power; // TODO: Support multiple power devices?
if (get_power == nullptr) { std::shared_ptr<hal::power::PowerDevice> power;
hal::findDevices<hal::power::PowerDevice>(hal::Device::Type::Power, [&power](const auto& device) {
if (device->supportsMetric(hal::power::PowerDevice::MetricType::ChargeLevel)) {
power = device;
return false;
}
return true;
});
if (power == nullptr) {
return nullptr; return nullptr;
} }
auto power = get_power();
hal::power::PowerDevice::MetricData charge_level; hal::power::PowerDevice::MetricData charge_level;
if (!power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) { if (!power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) {
return nullptr; return nullptr;