#ifdef ESP_PLATFORM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ESP_PLATFORM #include #endif namespace tt { static auto LOGGER = Logger("Tactility"); static const Configuration* config_instance = nullptr; static Dispatcher mainDispatcher; static struct ModuleParent tactility_module_parent { "tactility", nullptr }; ModuleParent& getModuleParent() { return tactility_module_parent; } // region Default services namespace service { // Primary namespace gps { extern const ServiceManifest manifest; } namespace wifi { extern const ServiceManifest manifest; } namespace sdcard { extern const ServiceManifest manifest; } #ifdef ESP_PLATFORM namespace development { extern const ServiceManifest manifest; } #endif #if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED) namespace espnow { extern const ServiceManifest manifest; } #endif // Secondary (UI) namespace gui { extern const ServiceManifest manifest; } namespace loader { extern const ServiceManifest manifest; } namespace memorychecker { extern const ServiceManifest manifest; } namespace statusbar { extern const ServiceManifest manifest; } #ifdef ESP_PLATFORM namespace displayidle { extern const ServiceManifest manifest; } #endif #if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK) namespace keyboardidle { extern const ServiceManifest manifest; } #endif #if TT_FEATURE_SCREENSHOT_ENABLED namespace screenshot { extern const ServiceManifest manifest; } #endif #ifdef ESP_PLATFORM namespace webserver { extern const ServiceManifest manifest; } #endif } // endregion // region Default apps namespace app { namespace addgps { extern const AppManifest manifest; } namespace alertdialog { extern const AppManifest manifest; } namespace apphub { extern const AppManifest manifest; } namespace apphubdetails { extern const AppManifest manifest; } namespace appdetails { extern const AppManifest manifest; } namespace applist { extern const AppManifest manifest; } namespace appsettings { extern const AppManifest manifest; } namespace boot { extern const AppManifest manifest; } namespace development { extern const AppManifest manifest; } namespace display { extern const AppManifest manifest; } namespace files { extern const AppManifest manifest; } namespace fileselection { extern const AppManifest manifest; } namespace gpssettings { extern const AppManifest manifest; } namespace i2cscanner { extern const AppManifest manifest; } namespace imageviewer { extern const AppManifest manifest; } namespace inputdialog { extern const AppManifest manifest; } namespace launcher { extern const AppManifest manifest; } namespace localesettings { extern const AppManifest manifest; } namespace notes { extern const AppManifest manifest; } namespace power { extern const AppManifest manifest; } namespace selectiondialog { extern const AppManifest manifest; } namespace settings { extern const AppManifest manifest; } namespace systeminfo { extern const AppManifest manifest; } namespace timedatesettings { extern const AppManifest manifest; } namespace timezone { extern const AppManifest manifest; } namespace usbsettings { extern const AppManifest manifest; } namespace wifiapsettings { extern const AppManifest manifest; } namespace wificonnect { extern const AppManifest manifest; } namespace wifimanage { extern const AppManifest manifest; } #ifdef ESP_PLATFORM namespace crashdiagnostics { extern const AppManifest manifest; } namespace webserversettings { extern const AppManifest manifest; } #endif #if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK) namespace keyboardsettings { extern const AppManifest manifest; } namespace trackballsettings { extern const AppManifest manifest; } #endif #if TT_FEATURE_SCREENSHOT_ENABLED namespace screenshot { extern const AppManifest manifest; } #endif #if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED) namespace chat { extern const AppManifest manifest; } #endif } // endregion // List of all apps excluding Boot app (as Boot app calls this function indirectly) static void registerInternalApps() { LOGGER.info("Registering internal apps"); addAppManifest(app::alertdialog::manifest); addAppManifest(app::appdetails::manifest); addAppManifest(app::apphub::manifest); addAppManifest(app::apphubdetails::manifest); addAppManifest(app::applist::manifest); addAppManifest(app::appsettings::manifest); addAppManifest(app::display::manifest); addAppManifest(app::files::manifest); addAppManifest(app::fileselection::manifest); addAppManifest(app::i2cscanner::manifest); addAppManifest(app::imageviewer::manifest); addAppManifest(app::inputdialog::manifest); addAppManifest(app::launcher::manifest); addAppManifest(app::localesettings::manifest); addAppManifest(app::notes::manifest); addAppManifest(app::settings::manifest); addAppManifest(app::selectiondialog::manifest); addAppManifest(app::systeminfo::manifest); addAppManifest(app::timedatesettings::manifest); addAppManifest(app::timezone::manifest); addAppManifest(app::wifiapsettings::manifest); addAppManifest(app::wificonnect::manifest); addAppManifest(app::wifimanage::manifest); #ifdef ESP_PLATFORM addAppManifest(app::webserversettings::manifest); addAppManifest(app::crashdiagnostics::manifest); addAppManifest(app::development::manifest); #endif #if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK) addAppManifest(app::keyboardsettings::manifest); addAppManifest(app::trackballsettings::manifest); #endif #if defined(CONFIG_TINYUSB_MSC_ENABLED) && CONFIG_TINYUSB_MSC_ENABLED addAppManifest(app::usbsettings::manifest); #endif #if TT_FEATURE_SCREENSHOT_ENABLED addAppManifest(app::screenshot::manifest); #endif #if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED) addAppManifest(app::chat::manifest); #endif if (!hal::getConfiguration()->uart.empty()) { addAppManifest(app::addgps::manifest); addAppManifest(app::gpssettings::manifest); } if (hal::hasDevice(hal::Device::Type::Power)) { addAppManifest(app::power::manifest); } } static void registerInstalledApp(std::string path) { LOGGER.info("Registering app at {}", path); std::string manifest_path = path + "/manifest.properties"; if (!file::isFile(manifest_path)) { LOGGER.error("Manifest not found at {}", manifest_path); return; } std::map properties; if (!file::loadPropertiesFile(manifest_path, properties)) { LOGGER.error("Failed to load manifest at {}", manifest_path); return; } app::AppManifest manifest; if (!app::parseManifest(properties, manifest)) { LOGGER.error("Failed to parse manifest at {}", manifest_path); return; } manifest.appCategory = app::Category::User; manifest.appLocation = app::Location::external(path); app::addAppManifest(manifest); } static void registerInstalledApps(const std::string& path) { LOGGER.info("Registering apps from {}", path); file::listDirectory(path, [&path](const auto& entry) { auto absolute_path = std::format("{}/{}", path, entry.d_name); if (file::isDirectory(absolute_path)) { registerInstalledApp(absolute_path); } }); } static void registerInstalledAppsFromSdCard(const std::shared_ptr& sdcard) { auto sdcard_root_path = sdcard->getMountPath(); auto app_path = std::format("{}/app", sdcard_root_path); if (file::isDirectory(app_path)) { registerInstalledApps(app_path); } } static void registerInstalledAppsFromSdCards() { auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); for (const auto& sdcard : sdcard_devices) { if (sdcard->isMounted()) { LOGGER.info("Registering apps from {}", sdcard->getMountPath()); registerInstalledAppsFromSdCard(sdcard); } } } static void registerAndStartSecondaryServices() { LOGGER.info("Registering and starting secondary system services"); addService(service::loader::manifest); addService(service::gui::manifest); addService(service::statusbar::manifest); #ifdef ESP_PLATFORM addService(service::displayidle::manifest); #endif #if defined(ESP_PLATFORM) && defined(CONFIG_TT_DEVICE_LILYGO_TDECK) addService(service::keyboardidle::manifest); #endif addService(service::memorychecker::manifest); #if TT_FEATURE_SCREENSHOT_ENABLED addService(service::screenshot::manifest); #endif } static void registerAndStartPrimaryServices() { LOGGER.info("Registering and starting primary system services"); addService(service::gps::manifest); if (hal::hasDevice(hal::Device::Type::SdCard)) { addService(service::sdcard::manifest); } addService(service::wifi::manifest); #ifdef ESP_PLATFORM addService(service::development::manifest); #endif #if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED) addService(service::espnow::manifest); #endif #ifdef ESP_PLATFORM addService(service::webserver::manifest); #endif } void createTempDirectory(const std::string& rootPath) { auto temp_path = std::format("{}/tmp", rootPath); if (!file::isDirectory(temp_path)) { auto lock = file::getLock(rootPath)->asScopedLock(); if (lock.lock(1000 / portTICK_PERIOD_MS)) { if (mkdir(temp_path.c_str(), 0777) == 0) { LOGGER.info("Created {}", temp_path); } else { LOGGER.error("Failed to create {}", temp_path); } } else { LOGGER.error(LOG_MESSAGE_MUTEX_LOCK_FAILED_FMT, rootPath); } } else { LOGGER.info("Found existing {}", temp_path); } } void prepareFileSystems() { // Temporary directories for SD cards auto sdcard_devices = hal::findDevices(hal::Device::Type::SdCard); for (const auto& sdcard : sdcard_devices) { if (sdcard->isMounted()) { createTempDirectory(sdcard->getMountPath()); } } // Temporary directory for /data if (file::isDirectory(file::MOUNT_POINT_DATA)) { createTempDirectory(file::MOUNT_POINT_DATA); } } void registerApps() { registerInternalApps(); auto data_apps_path = std::format("{}/app", file::MOUNT_POINT_DATA); if (file::isDirectory(data_apps_path)) { registerInstalledApps(data_apps_path); } registerInstalledAppsFromSdCards(); } void run(const Configuration& config, Module* platformModule, Module* deviceModule, CompatibleDevice devicetreeDevices[]) { LOGGER.info("Tactility v{} on {} ({})", TT_VERSION, CONFIG_TT_DEVICE_NAME, CONFIG_TT_DEVICE_ID); assert(config.hardware); LOGGER.info(R"(Calling kernel_init with modules: "{}" and "{}")", platformModule->name, deviceModule->name); if (kernel_init(platformModule, deviceModule, devicetreeDevices) != ERROR_NONE) { LOGGER.error("Failed to initialize kernel"); return; } // Module parent check(module_parent_construct(&tactility_module_parent) == ERROR_NONE); // hal-device-module check(module_set_parent(&hal_device_module, &tactility_module_parent) == ERROR_NONE); check(module_start(&hal_device_module) == ERROR_NONE); const hal::Configuration& hardware = *config.hardware; // Assign early so starting services can use it config_instance = &config; #ifdef ESP_PLATFORM initEsp(); #endif file::setFindLockFunction(file::findLock); settings::initTimeZone(); hal::init(*config.hardware); network::ntp::init(); registerAndStartPrimaryServices(); lvgl_module_configure((LvglModuleConfig) { .on_start = lvgl::attachDevices, .on_stop = lvgl::detachDevices, .task_priority = THREAD_PRIORITY_HIGHER, /** Minimum seems to be about 3500. In some scenarios, the WiFi app crashes at 8192, * so we now have 9120 to run in a stable manner. We should figure out a way to avoid this. * Perhaps we can give apps their own stack space and deal with lvgl callback handlers in a clever way. */ .task_stack_size = 9120, #ifdef ESP_PLATFORM .task_affinity = getCpuAffinityConfiguration().graphics #endif }); check(module_set_parent(&lvgl_module, &tactility_module_parent) == ERROR_NONE); lvgl::start(); registerAndStartSecondaryServices(); LOGGER.info("Core systems ready"); LOGGER.info("Starting boot app"); // The boot app takes care of registering system apps, user services and user apps addAppManifest(app::boot::manifest); app::start(app::boot::manifest.appId); LOGGER.info("Main dispatcher ready"); while (true) { mainDispatcher.consume(); } } /** return the configuration or nullptr if it's not initialized */ const Configuration* getConfiguration() { return config_instance; } Dispatcher& getMainDispatcher() { return mainDispatcher; } } // namespace