Project restructuring: add tactility-headless (#55)
- Created `tactility-headless` to support ESP32 firmwares that don't require graphics - `tactility` subproject now contains both PC and ESP32 code (to avoid having to split up `tactility` and `tactility-headless` into separate projects, which would result in a very complex dependency tree) - `tactility` subproject is now defined as component for ESP32 and as regular module for PC - Improvements for dispatcher - Added `project-structure.puml` to docs - `Gui` service now depends on `Loader` service instead of the reverse - Added `statusbar_updater` service for updating Wi-Fi and SD card icons
This commit is contained in:
parent
b659d5b940
commit
27730260e0
@ -15,8 +15,9 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
set(COMPONENTS app-esp)
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"boards"
|
||||
"tactility-esp"
|
||||
"app-esp"
|
||||
"tactility"
|
||||
"tactility-headless"
|
||||
"libs/esp_lvgl_port"
|
||||
"libs/lvgl"
|
||||
"libs/M5Unified"
|
||||
@ -40,7 +41,12 @@ endif()
|
||||
|
||||
project(tactility-root)
|
||||
|
||||
add_subdirectory(tactility)
|
||||
# Defined as regular project for PC and component for ESP
|
||||
if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_subdirectory(tactility)
|
||||
add_subdirectory(tactility-headless)
|
||||
endif()
|
||||
|
||||
add_subdirectory(tactility-core)
|
||||
|
||||
add_subdirectory(libs/mlib)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(BOARD_COMPONENTS tactility-esp)
|
||||
set(BOARD_COMPONENTS tactility)
|
||||
|
||||
if("${IDF_TARGET}" STREQUAL "esp32")
|
||||
list(APPEND BOARD_COMPONENTS
|
||||
@ -22,7 +22,3 @@ idf_component_register(
|
||||
"src/hello_world"
|
||||
REQUIRES ${BOARD_COMPONENTS}
|
||||
)
|
||||
|
||||
# TODO Remove?
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility)
|
||||
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
#include "board_config.h"
|
||||
#include "tactility_esp.h"
|
||||
|
||||
// Apps
|
||||
#include "hello_world/hello_world.h"
|
||||
#include "tactility.h"
|
||||
|
||||
extern void wifi_main(void*);
|
||||
|
||||
extern const ServiceManifest wifi_service;
|
||||
extern const AppManifest gpio_app;
|
||||
extern const AppManifest wifi_connect_app;
|
||||
extern const AppManifest wifi_manage_app;
|
||||
|
||||
void app_main(void) {
|
||||
static const Config config = {
|
||||
/**
|
||||
@ -19,19 +14,12 @@ void app_main(void) {
|
||||
*/
|
||||
.hardware = TT_BOARD_HARDWARE,
|
||||
.apps = {
|
||||
&gpio_app,
|
||||
&hello_world_app,
|
||||
&wifi_connect_app,
|
||||
&wifi_manage_app
|
||||
},
|
||||
.services = {
|
||||
&wifi_service
|
||||
},
|
||||
.services = {},
|
||||
.auto_start_app_id = NULL
|
||||
};
|
||||
|
||||
tt_esp_init();
|
||||
|
||||
tt_init(&config);
|
||||
|
||||
wifi_main(NULL);
|
||||
|
||||
@ -5,6 +5,7 @@ add_executable(app-sim ${SOURCES})
|
||||
target_link_libraries(app-sim
|
||||
PRIVATE tactility
|
||||
PRIVATE tactility-core
|
||||
PRIVATE tactility-headless
|
||||
PRIVATE lvgl
|
||||
)
|
||||
|
||||
|
||||
@ -24,5 +24,5 @@ TT_UNUSED static void lvgl_deinit() {
|
||||
|
||||
HardwareConfig sim_hardware = {
|
||||
.bootstrap = NULL,
|
||||
.init_lvgl = &lvgl_init,
|
||||
.init_graphics = &lvgl_init,
|
||||
};
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
#include "hello_world/hello_world.h"
|
||||
#include "tactility.h"
|
||||
#include "assets.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "ui/statusbar.h"
|
||||
|
||||
#define TAG "main"
|
||||
|
||||
extern HardwareConfig sim_hardware;
|
||||
|
||||
@ -20,9 +14,4 @@ void app_main() {
|
||||
};
|
||||
|
||||
tt_init(&config);
|
||||
|
||||
// Note: this is just to test the statusbar as Wi-Fi
|
||||
// and sd card apps are not available for PC
|
||||
tt_statusbar_icon_add(TT_ASSETS_ICON_SDCARD_ALERT);
|
||||
tt_statusbar_icon_add(TT_ASSETS_ICON_WIFI_OFF);
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch_gt911 driver vfs fatfs
|
||||
REQUIRES tactility esp_lvgl_port esp_lcd esp_lcd_touch_gt911 driver vfs fatfs
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility)
|
||||
@ -12,6 +12,6 @@ const HardwareConfig lilygo_tdeck = {
|
||||
.display = {
|
||||
.set_backlight_duty = &tdeck_backlight_set
|
||||
},
|
||||
.init_lvgl = &tdeck_init_lvgl,
|
||||
.init_graphics = &tdeck_init_lvgl,
|
||||
.sdcard = &tdeck_sdcard
|
||||
};
|
||||
|
||||
@ -2,7 +2,5 @@ idf_component_register(
|
||||
SRC_DIRS "source"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "private"
|
||||
REQUIRES esp_lvgl_port esp_lcd_ili9341 driver vfs fatfs M5Unified
|
||||
REQUIRES tactility esp_lvgl_port esp_lcd_ili9341 driver vfs fatfs M5Unified
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility)
|
||||
|
||||
@ -9,6 +9,6 @@ const HardwareConfig m5stack_core2 = {
|
||||
.display = {
|
||||
.set_backlight_duty = NULL
|
||||
},
|
||||
.init_lvgl = &core2_lvgl_init,
|
||||
.init_graphics = &core2_lvgl_init,
|
||||
.sdcard = &core2_sdcard
|
||||
};
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES lvgl esp_lcd esp_lcd_touch_gt911
|
||||
REQUIRES tactility lvgl esp_lcd esp_lcd_touch_gt911
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility)
|
||||
@ -9,5 +9,5 @@ const HardwareConfig waveshare_s3_touch = {
|
||||
.display = {
|
||||
.set_backlight_duty = NULL // TODO: This requires implementing the CH422G IO expander
|
||||
},
|
||||
.init_lvgl = &ws3t_init_lvgl
|
||||
.init_graphics = &ws3t_init_lvgl
|
||||
};
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_lvgl_port esp_lcd_touch_cst816s esp_lcd_ili9341 driver vfs fatfs
|
||||
REQUIRES tactility esp_lvgl_port esp_lcd_touch_cst816s esp_lcd_ili9341 driver vfs fatfs
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility)
|
||||
@ -11,6 +11,6 @@ const HardwareConfig yellow_board_24inch_cap = {
|
||||
.display = {
|
||||
.set_backlight_duty = &twodotfour_backlight_set
|
||||
},
|
||||
.init_lvgl = &twodotfour_lvgl_init,
|
||||
.init_graphics = &twodotfour_lvgl_init,
|
||||
.sdcard = &twodotfour_sdcard
|
||||
};
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
- App lifecycle docs mention on_create/on_destroy but app lifecycle is on_start/on_stop
|
||||
- Explore LVGL9's FreeRTOS functionality
|
||||
- Explore LVGL9's ILI93414 driver for 2.4" Yellow Board
|
||||
|
||||
|
||||
# Core Ideas
|
||||
- Make a HAL? It would mainly be there to support PC development. It's a lot of effort for supporting what's effectively a dev-only feature.
|
||||
- Support for displays with different DPI. Consider the layer-based system like on Android.
|
||||
@ -22,7 +22,7 @@
|
||||
- 2 wire speaker support
|
||||
- tt_app_start() and similar functions as proxies for Loader app start/stop/etc.
|
||||
- tt_app_set_result() for apps that need to return data to other apps (e.g. file selection)
|
||||
- Introduce co-routines for calling wifi/lvgl/etc functionality.
|
||||
- Wi-Fi using dispatcher to dispatch its main functionality to the dedicated Wi-Fi CPU core (to avoid main loop hack)
|
||||
|
||||
# App Improvement Ideas
|
||||
- Sort desktop apps by name.
|
||||
@ -30,7 +30,6 @@
|
||||
|
||||
# App Ideas
|
||||
- File viewer (images, text... binary?)
|
||||
- GPIO status viewer
|
||||
- BlueTooth keyboard app
|
||||
- Chip 8 emulator
|
||||
- BadUSB
|
||||
|
||||
23
docs/project-structure.puml
Normal file
23
docs/project-structure.puml
Normal file
@ -0,0 +1,23 @@
|
||||
@startuml
|
||||
skinparam componentStyle uml1
|
||||
|
||||
[tactility] as t
|
||||
note right of t : to build and use graphical apps
|
||||
[tactility-headless] as theadless
|
||||
note right of theadless : to build and use background services
|
||||
[tactility-core] as tcore
|
||||
note right of tcore : defines, data types, logging, async, etc.
|
||||
|
||||
package "Applications" {
|
||||
[app-sim] as appsim
|
||||
[app-esp] as appesp
|
||||
}
|
||||
|
||||
note right of appesp : app-esp depends on the board \n projects for configuration
|
||||
|
||||
[t] ..> [theadless]
|
||||
[theadless] ..> [tcore]
|
||||
[appsim] ..> [t]
|
||||
[appesp] ..> [t]
|
||||
|
||||
@enduml
|
||||
@ -8,6 +8,7 @@ file(GLOB SOURCES "src/*.c")
|
||||
file(GLOB HEADERS "src/*.h")
|
||||
|
||||
add_library(tactility-core OBJECT)
|
||||
|
||||
target_sources(tactility-core
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
|
||||
@ -2,9 +2,20 @@
|
||||
|
||||
#include "tactility_core.h"
|
||||
|
||||
typedef struct {
|
||||
Callback callback;
|
||||
void* context;
|
||||
} DispatcherMessage;
|
||||
|
||||
typedef struct {
|
||||
MessageQueue* queue;
|
||||
Mutex* mutex;
|
||||
DispatcherMessage buffer; // Buffer for consuming a message
|
||||
} DispatcherData;
|
||||
|
||||
Dispatcher* tt_dispatcher_alloc(uint32_t message_count) {
|
||||
Dispatcher* dispatcher = malloc(sizeof(Dispatcher));
|
||||
*dispatcher = (Dispatcher) {
|
||||
DispatcherData* data = malloc(sizeof(DispatcherData));
|
||||
*data = (DispatcherData) {
|
||||
.queue = tt_message_queue_alloc(message_count, sizeof(DispatcherMessage)),
|
||||
.mutex = tt_mutex_alloc(MutexTypeNormal),
|
||||
.buffer = {
|
||||
@ -12,37 +23,40 @@ Dispatcher* tt_dispatcher_alloc(uint32_t message_count) {
|
||||
.context = NULL
|
||||
}
|
||||
};
|
||||
return dispatcher;
|
||||
return data;
|
||||
}
|
||||
|
||||
void tt_dispatcher_free(Dispatcher* dispatcher) {
|
||||
tt_mutex_acquire(dispatcher->mutex, TtWaitForever);
|
||||
tt_message_queue_reset(dispatcher->queue);
|
||||
tt_message_queue_free(dispatcher->queue);
|
||||
tt_mutex_release(dispatcher->mutex);
|
||||
tt_mutex_free(dispatcher->mutex);
|
||||
free(dispatcher);
|
||||
DispatcherData* data = (DispatcherData*)dispatcher;
|
||||
tt_mutex_acquire(data->mutex, TtWaitForever);
|
||||
tt_message_queue_reset(data->queue);
|
||||
tt_message_queue_free(data->queue);
|
||||
tt_mutex_release(data->mutex);
|
||||
tt_mutex_free(data->mutex);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void tt_dispatcher_dispatch(Dispatcher* dispatcher, Callback callback, void* context) {
|
||||
DispatcherData* data = (DispatcherData*)dispatcher;
|
||||
DispatcherMessage message = {
|
||||
.callback = callback,
|
||||
.context = context
|
||||
};
|
||||
tt_mutex_acquire(dispatcher->mutex, TtWaitForever);
|
||||
tt_message_queue_put(dispatcher->queue, &message, TtWaitForever);
|
||||
tt_mutex_release(dispatcher->mutex);
|
||||
tt_mutex_acquire(data->mutex, TtWaitForever);
|
||||
tt_message_queue_put(data->queue, &message, TtWaitForever);
|
||||
tt_mutex_release(data->mutex);
|
||||
}
|
||||
|
||||
bool tt_dispatcher_consume(Dispatcher* dispatcher, uint32_t timeout_ticks) {
|
||||
tt_mutex_acquire(dispatcher->mutex, TtWaitForever);
|
||||
if (tt_message_queue_get(dispatcher->queue, &(dispatcher->buffer), timeout_ticks) == TtStatusOk) {
|
||||
DispatcherMessage* message = &(dispatcher->buffer);
|
||||
DispatcherData* data = (DispatcherData*)dispatcher;
|
||||
tt_mutex_acquire(data->mutex, TtWaitForever);
|
||||
if (tt_message_queue_get(data->queue, &(data->buffer), timeout_ticks) == TtStatusOk) {
|
||||
DispatcherMessage* message = &(data->buffer);
|
||||
message->callback(message->context);
|
||||
tt_mutex_release(dispatcher->mutex);
|
||||
tt_mutex_release(data->mutex);
|
||||
return true;
|
||||
} else {
|
||||
tt_mutex_release(dispatcher->mutex);
|
||||
tt_mutex_release(data->mutex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file message_queue.h
|
||||
* @file dispatcher.h
|
||||
*
|
||||
* Dispatcher is a thread-safe message queue implementation for callbacks.
|
||||
* Dispatcher is a thread-safe code execution queue.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@ -15,21 +15,12 @@ extern "C" {
|
||||
|
||||
typedef void (*Callback)(void* data);
|
||||
|
||||
typedef struct {
|
||||
Callback callback;
|
||||
void* context;
|
||||
} DispatcherMessage;
|
||||
|
||||
typedef struct {
|
||||
MessageQueue* queue;
|
||||
Mutex* mutex;
|
||||
DispatcherMessage buffer; // Buffer for consuming a message
|
||||
} Dispatcher;
|
||||
typedef void Dispatcher;
|
||||
|
||||
Dispatcher* tt_dispatcher_alloc(uint32_t message_count);
|
||||
void tt_dispatcher_free(Dispatcher* dispatcher);
|
||||
void tt_dispatcher_dispatch(Dispatcher* dispatcher, Callback callback, void* context);
|
||||
bool tt_dispatcher_consume(Dispatcher* dispatcher, uint32_t timeout_ticks);
|
||||
void tt_dispatcher_dispatch(Dispatcher* data, Callback callback, void* context);
|
||||
bool tt_dispatcher_consume(Dispatcher* data, uint32_t timeout_ticks);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#ifdef ESP_TARGET
|
||||
#include "esp_log.h"
|
||||
#else
|
||||
#include <stdarg.h>
|
||||
@ -11,7 +11,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#define TT_LOG_E(tag, format, ...) \
|
||||
ESP_LOGE(tag, format, ##__VA_ARGS__)
|
||||
@ -47,7 +47,7 @@ void tt_log(LogLevel level, const char* tag, const char* format, ...);
|
||||
#define TT_LOG_T(tag, format, ...) \
|
||||
tt_log(LOG_LEVEL_TRACE, tag, format, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
#endif // ESP_TARGET
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(BOARD_COMPONENTS esp_wifi)
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES src/*.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "src/"
|
||||
REQUIRES esp_wifi nvs_flash spiffs
|
||||
)
|
||||
|
||||
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../data/assets")
|
||||
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT)
|
||||
|
||||
set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../data/config")
|
||||
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility)
|
||||
target_link_libraries(${COMPONENT_LIB} ${IDF_TARGET_NAME} tactility-core)
|
||||
|
||||
48
tactility-headless/CMakeLists.txt
Normal file
48
tactility-headless/CMakeLists.txt
Normal file
@ -0,0 +1,48 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
file(GLOB_RECURSE SOURCE_FILES src/*.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "src/"
|
||||
REQUIRES esp_wifi nvs_flash spiffs driver newlib
|
||||
)
|
||||
|
||||
set(ASSETS_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../data/assets")
|
||||
spiffs_create_partition_image(assets ${ASSETS_SRC_DIR} FLASH_IN_PROJECT)
|
||||
|
||||
set(CONFIG_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../data/config")
|
||||
spiffs_create_partition_image(config ${CONFIG_SRC_DIR} FLASH_IN_PROJECT)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB}
|
||||
PUBLIC tactility-core
|
||||
)
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
else()
|
||||
file(GLOB_RECURSE SOURCES "src/*.c")
|
||||
file(GLOB_RECURSE HEADERS "src/*.h")
|
||||
|
||||
add_library(tactility-headless OBJECT)
|
||||
target_sources(tactility-headless
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(tactility-headless
|
||||
PRIVATE src/
|
||||
INTERFACE src/
|
||||
)
|
||||
|
||||
add_definitions(-D_Nullable=)
|
||||
add_definitions(-D_Nonnull=)
|
||||
target_link_libraries(tactility-headless
|
||||
PUBLIC tactility-core
|
||||
PUBLIC freertos_kernel
|
||||
)
|
||||
endif()
|
||||
@ -1,14 +1,18 @@
|
||||
#include "tactility_core.h"
|
||||
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "esp_partitions.h"
|
||||
#include "services/wifi/wifi_credentials.h"
|
||||
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "partitions.h"
|
||||
#include "services/wifi/wifi_credentials.h"
|
||||
|
||||
#define TAG "tactility"
|
||||
void tt_esp_init() {
|
||||
// Initialize NVS
|
||||
|
||||
// Initialize NVS
|
||||
static void tt_esp_nvs_init() {
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
TT_LOG_I(TAG, "nvs erasing");
|
||||
@ -17,12 +21,20 @@ void tt_esp_init() {
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
TT_LOG_I(TAG, "nvs initialized");
|
||||
}
|
||||
|
||||
// Network interface
|
||||
static void tt_esp_network_init() {
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
}
|
||||
|
||||
tt_partitions_init();
|
||||
void tt_esp_init() {
|
||||
tt_esp_nvs_init();
|
||||
tt_esp_partitions_init();
|
||||
|
||||
tt_esp_network_init();
|
||||
|
||||
tt_wifi_credentials_init();
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "hardware_config.h"
|
||||
#include "tactility.h"
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -12,3 +11,5 @@ void tt_esp_init();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ESP_TARGET
|
||||
@ -1,4 +1,6 @@
|
||||
#include "partitions.h"
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "esp_partitions.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "log.h"
|
||||
#include "nvs_flash.h"
|
||||
@ -37,7 +39,7 @@ static esp_err_t spiffs_init(esp_vfs_spiffs_conf_t* conf) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t tt_partitions_init() {
|
||||
esp_err_t tt_esp_partitions_init() {
|
||||
ESP_ERROR_CHECK(nvs_flash_init_safely());
|
||||
|
||||
esp_vfs_spiffs_conf_t assets_spiffs = {
|
||||
@ -64,3 +66,5 @@ esp_err_t tt_partitions_init() {
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // ESP_TARGET
|
||||
@ -1,8 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#define MOUNT_POINT_ASSETS "/assets"
|
||||
#define MOUNT_POINT_CONFIG "/config"
|
||||
|
||||
esp_err_t tt_partitions_init();
|
||||
esp_err_t tt_esp_partitions_init();
|
||||
|
||||
#endif // ESP_TARGET
|
||||
22
tactility-headless/src/hardware.c
Normal file
22
tactility-headless/src/hardware.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include "hardware_i.h"
|
||||
|
||||
#include "preferences.h"
|
||||
#include "sdcard_i.h"
|
||||
|
||||
#define TAG "hardware"
|
||||
|
||||
void tt_hardware_init(const HardwareConfig* config) {
|
||||
if (config->bootstrap != NULL) {
|
||||
TT_LOG_I(TAG, "Bootstrapping");
|
||||
tt_check(config->bootstrap(), "bootstrap failed");
|
||||
}
|
||||
|
||||
tt_sdcard_init();
|
||||
if (config->sdcard != NULL) {
|
||||
TT_LOG_I(TAG, "Mounting sdcard");
|
||||
tt_sdcard_mount(config->sdcard);
|
||||
}
|
||||
|
||||
tt_check(config->init_graphics, "lvlg init not set");
|
||||
tt_check(config->init_graphics(), "lvgl init failed");
|
||||
}
|
||||
@ -4,8 +4,7 @@
|
||||
#include "sdcard.h"
|
||||
|
||||
typedef bool (*Bootstrap)();
|
||||
typedef bool (*InitLvgl)();
|
||||
typedef bool (*InitLvgl)();
|
||||
typedef bool (*InitGraphics)();
|
||||
|
||||
typedef void (*SetBacklightDuty)(uint8_t);
|
||||
typedef struct {
|
||||
@ -24,10 +23,11 @@ typedef struct {
|
||||
* Initializes LVGL with all relevant hardware.
|
||||
* This includes the display and optional pointer devices (such as touch) or a keyboard.
|
||||
*/
|
||||
const InitLvgl init_lvgl;
|
||||
const InitGraphics init_graphics;
|
||||
|
||||
/**
|
||||
* An interface for display features such as setting the backlight.
|
||||
* This does nothing when a display isn't present.
|
||||
*/
|
||||
const Display display;
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
#include "nvs_flash.h"
|
||||
#include "tactility_core.h"
|
||||
|
||||
#define TAG "preferences"
|
||||
|
||||
static bool opt_bool(const char* namespace, const char* key, bool* out) {
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(namespace, NVS_READWRITE, &handle) != ESP_OK) {
|
||||
@ -60,16 +62,24 @@ static bool has_string(const char* namespace, const char* key) {
|
||||
static void put_bool(const char* namespace, const char* key, bool value) {
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(namespace, NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_u8(handle, key, (uint8_t)value) == ESP_OK;
|
||||
if (nvs_set_u8(handle, key, (uint8_t)value) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "failed to write %s:%s", namespace, key);
|
||||
}
|
||||
nvs_close(handle);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_int32(const char* namespace, const char* key, int32_t value) {
|
||||
nvs_handle_t handle;
|
||||
if (nvs_open(namespace, NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_i32(handle, key, value) == ESP_OK;
|
||||
if (nvs_set_i32(handle, key, value) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "failed to write %s:%s", namespace, key);
|
||||
}
|
||||
nvs_close(handle);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +88,8 @@ static void put_string(const char* namespace, const char* key, const char* text)
|
||||
if (nvs_open(namespace, NVS_READWRITE, &handle) == ESP_OK) {
|
||||
nvs_set_str(handle, key, text);
|
||||
nvs_close(handle);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "failed to open namespace %s for writing", namespace);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
#include <dirent.h>
|
||||
|
||||
#include "assets.h"
|
||||
#include "mutex.h"
|
||||
#include "service.h"
|
||||
#include "tactility.h"
|
||||
#include "tactility_core.h"
|
||||
#include "ui/statusbar.h"
|
||||
#include "tactility_headless.h"
|
||||
|
||||
#define TAG "sdcard_service"
|
||||
|
||||
@ -15,7 +13,6 @@ typedef struct {
|
||||
Mutex* mutex;
|
||||
Thread* thread;
|
||||
SdcardState last_state;
|
||||
int8_t statusbar_icon_id;
|
||||
bool interrupted;
|
||||
} ServiceData;
|
||||
|
||||
@ -30,7 +27,6 @@ static ServiceData* service_data_alloc() {
|
||||
data
|
||||
),
|
||||
.last_state = -1,
|
||||
.statusbar_icon_id = tt_statusbar_icon_add(NULL),
|
||||
.interrupted = false
|
||||
};
|
||||
tt_thread_set_priority(data->thread, ThreadPriorityLow);
|
||||
@ -39,7 +35,6 @@ static ServiceData* service_data_alloc() {
|
||||
|
||||
static void service_data_free(ServiceData* data) {
|
||||
tt_mutex_free(data->mutex);
|
||||
tt_statusbar_icon_remove(data->statusbar_icon_id);
|
||||
tt_thread_free(data->thread);
|
||||
}
|
||||
|
||||
@ -56,9 +51,6 @@ static int32_t sdcard_task(void* context) {
|
||||
|
||||
bool interrupted = false;
|
||||
|
||||
// We set NULL as statusbar image by default, so it's hidden by default
|
||||
tt_statusbar_icon_set_visibility(data->statusbar_icon_id, true);
|
||||
|
||||
do {
|
||||
service_data_lock(data);
|
||||
|
||||
@ -72,12 +64,6 @@ static int32_t sdcard_task(void* context) {
|
||||
}
|
||||
|
||||
if (new_state != data->last_state) {
|
||||
TT_LOG_I(TAG, "State change %d -> %d", data->last_state, new_state);
|
||||
if (new_state == SdcardStateMounted) {
|
||||
tt_statusbar_icon_set_image(data->statusbar_icon_id, TT_ASSETS_ICON_SDCARD);
|
||||
} else {
|
||||
tt_statusbar_icon_set_image(data->statusbar_icon_id, TT_ASSETS_ICON_SDCARD_ALERT);
|
||||
}
|
||||
data->last_state = new_state;
|
||||
}
|
||||
|
||||
@ -89,7 +75,7 @@ static int32_t sdcard_task(void* context) {
|
||||
}
|
||||
|
||||
static void on_start(Service service) {
|
||||
if (tt_get_config()->hardware->sdcard != NULL) {
|
||||
if (tt_get_hardware_config()->sdcard != NULL) {
|
||||
ServiceData* data = service_data_alloc();
|
||||
tt_service_set_data(service, data);
|
||||
tt_thread_start(data->thread);
|
||||
9
tactility-headless/src/services/sdcard/sdcard.h
Normal file
9
tactility-headless/src/services/sdcard/sdcard.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -10,6 +10,26 @@ extern "C" {
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include "esp_wifi.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
// From esp_wifi_types.h in ESP-IDF 5.2
|
||||
typedef enum {
|
||||
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
|
||||
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
|
||||
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
|
||||
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
|
||||
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
|
||||
WIFI_AUTH_ENTERPRISE, /**< authenticate mode : WiFi EAP security */
|
||||
WIFI_AUTH_WPA2_ENTERPRISE = WIFI_AUTH_ENTERPRISE, /**< authenticate mode : WiFi EAP security */
|
||||
WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */
|
||||
WIFI_AUTH_WPA2_WPA3_PSK, /**< authenticate mode : WPA2_WPA3_PSK */
|
||||
WIFI_AUTH_WAPI_PSK, /**< authenticate mode : WAPI_PSK */
|
||||
WIFI_AUTH_OWE, /**< authenticate mode : OWE */
|
||||
WIFI_AUTH_WPA3_ENT_192, /**< authenticate mode : WPA3_ENT_SUITE_B_192_BIT */
|
||||
WIFI_AUTH_WPA3_EXT_PSK, /**< authenticate mode : WPA3_PSK_EXT_KEY */
|
||||
WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE, /**< authenticate mode: WPA3_PSK + WPA3_PSK_EXT_KEY */
|
||||
WIFI_AUTH_MAX
|
||||
} wifi_auth_mode_t;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
@ -100,12 +120,14 @@ void wifi_connect(const char* ssid, const char _Nullable password[64]);
|
||||
void wifi_disconnect();
|
||||
|
||||
/**
|
||||
* Return the relevant icon asset from assets.h for the given inputs
|
||||
* @param rssi the rssi value
|
||||
* @param secured whether the access point is a secured one (as in: not an open one)
|
||||
* @return
|
||||
* Return true if the connection isn't unencrypted.
|
||||
*/
|
||||
const char* wifi_get_status_icon_for_rssi(int rssi, bool secured);
|
||||
bool wifi_is_connection_secure();
|
||||
|
||||
/**
|
||||
* Returns the RSSI value (negative number) or return 1 when not connected
|
||||
*/
|
||||
int wifi_get_rssi();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@ -22,4 +22,4 @@ bool tt_wifi_credentials_remove(const char* ssid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@ -1,3 +1,5 @@
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "wifi_credentials.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
@ -230,3 +232,4 @@ bool tt_wifi_credentials_remove(const char* ssid) {
|
||||
|
||||
// end region Wi-Fi Credentials - public
|
||||
|
||||
#endif // ESP_TARGET
|
||||
30
tactility-headless/src/services/wifi/wifi_credentials_mock.c
Normal file
30
tactility-headless/src/services/wifi/wifi_credentials_mock.c
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef ESP_TARGET
|
||||
|
||||
#include "wifi_credentials.h"
|
||||
#include "log.h"
|
||||
|
||||
#define TAG "wifi_credentials_mock"
|
||||
|
||||
static void hash_reset_all();
|
||||
|
||||
bool tt_wifi_credentials_contains(const char* ssid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void tt_wifi_credentials_init() {
|
||||
TT_LOG_I(TAG, "init");
|
||||
}
|
||||
|
||||
bool tt_wifi_credentials_get(const char* ssid, char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tt_wifi_credentials_set(const char* ssid, char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tt_wifi_credentials_remove(const char* ssid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // ESP_TARGET
|
||||
@ -1,3 +1,5 @@
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "wifi.h"
|
||||
|
||||
#include "assets.h"
|
||||
@ -9,7 +11,6 @@
|
||||
#include "mutex.h"
|
||||
#include "pubsub.h"
|
||||
#include "service.h"
|
||||
#include "ui/statusbar.h"
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define TAG "wifi"
|
||||
@ -32,14 +33,12 @@ typedef struct {
|
||||
uint16_t scan_list_count;
|
||||
/** @brief Maximum amount of records to scan (value > 0) */
|
||||
uint16_t scan_list_limit;
|
||||
int8_t statusbar_icon_id;
|
||||
bool scan_active;
|
||||
bool secure_connection;
|
||||
esp_event_handler_instance_t event_handler_any_id;
|
||||
esp_event_handler_instance_t event_handler_got_ip;
|
||||
EventGroupHandle_t event_group;
|
||||
WifiRadioState radio_state;
|
||||
const char* _Nullable last_statusbar_icon;
|
||||
} Wifi;
|
||||
|
||||
typedef enum {
|
||||
@ -89,14 +88,11 @@ static Wifi* wifi_alloc() {
|
||||
instance->event_handler_got_ip = NULL;
|
||||
instance->event_group = xEventGroupCreate();
|
||||
instance->radio_state = WIFI_RADIO_OFF;
|
||||
instance->statusbar_icon_id = tt_statusbar_icon_add(TT_ASSETS_ICON_WIFI_OFF);
|
||||
instance->last_statusbar_icon = NULL;
|
||||
instance->secure_connection = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void wifi_free(Wifi* instance) {
|
||||
tt_statusbar_icon_remove(instance->statusbar_icon_id);
|
||||
tt_mutex_free(instance->mutex);
|
||||
tt_pubsub_free(instance->pubsub);
|
||||
tt_message_queue_free(instance->queue);
|
||||
@ -188,10 +184,24 @@ void wifi_set_enabled(bool enabled) {
|
||||
}
|
||||
}
|
||||
|
||||
bool wifi_is_connection_secure() {
|
||||
tt_check(wifi_singleton);
|
||||
return wifi_singleton->secure_connection;
|
||||
}
|
||||
|
||||
int wifi_get_rssi() {
|
||||
static int rssi = 0;
|
||||
if (esp_wifi_sta_get_rssi(&rssi) == ESP_OK) {
|
||||
return rssi;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// endregion Public functions
|
||||
|
||||
static void wifi_lock(Wifi* wifi) {
|
||||
tt_crash("this fails for now");
|
||||
tt_crash("this fails for now"); // TODO: Fix
|
||||
tt_assert(wifi);
|
||||
tt_assert(wifi->mutex);
|
||||
tt_check(xSemaphoreTakeRecursive(wifi->mutex, portMAX_DELAY) == pdPASS);
|
||||
@ -422,47 +432,6 @@ static void wifi_scan_internal(Wifi* wifi) {
|
||||
TT_LOG_I(TAG, "Finished scan");
|
||||
}
|
||||
|
||||
const char* wifi_get_status_icon_for_rssi(int rssi, bool secured) {
|
||||
if (rssi > -67) {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_4_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_4;
|
||||
} else if (rssi > -70) {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_3_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_3;
|
||||
} else if (rssi > -80) {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_2_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_2;
|
||||
} else {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_1_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_1;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* wifi_get_status_icon(WifiRadioState state, bool secure) {
|
||||
static int rssi = 0;
|
||||
switch (state) {
|
||||
case WIFI_RADIO_ON_PENDING:
|
||||
case WIFI_RADIO_ON:
|
||||
case WIFI_RADIO_OFF_PENDING:
|
||||
case WIFI_RADIO_OFF:
|
||||
return TT_ASSETS_ICON_WIFI_OFF;
|
||||
case WIFI_RADIO_CONNECTION_PENDING:
|
||||
return TT_ASSETS_ICON_WIFI_FIND;
|
||||
case WIFI_RADIO_CONNECTION_ACTIVE:
|
||||
if (esp_wifi_sta_get_rssi(&rssi) == ESP_OK) {
|
||||
return wifi_get_status_icon_for_rssi(rssi, secure);
|
||||
} else {
|
||||
return secure ? TT_ASSETS_ICON_WIFI_SIGNAL_0_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_0;
|
||||
}
|
||||
default:
|
||||
tt_crash_implementation("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_update_statusbar(Wifi* wifi) {
|
||||
const char* icon = wifi_get_status_icon(wifi->radio_state, wifi->secure_connection);
|
||||
if (icon != wifi->last_statusbar_icon) {
|
||||
tt_statusbar_icon_set_image(wifi->statusbar_icon_id, icon);
|
||||
wifi->last_statusbar_icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_connect_internal(Wifi* wifi, WifiConnectMessage* connect_message) {
|
||||
// TODO: only when connected!
|
||||
wifi_disconnect_internal(wifi);
|
||||
@ -536,6 +505,7 @@ static void wifi_disconnect_internal(Wifi* wifi) {
|
||||
TT_LOG_E(TAG, "Failed to disconnect (%s)", esp_err_to_name(stop_result));
|
||||
} else {
|
||||
wifi->radio_state = WIFI_RADIO_ON;
|
||||
wifi->secure_connection = false;
|
||||
wifi_publish_event_simple(wifi, WifiEventTypeDisconnected);
|
||||
TT_LOG_I(TAG, "Disconnected");
|
||||
}
|
||||
@ -610,8 +580,6 @@ _Noreturn int32_t wifi_main(TT_UNUSED void* parameter) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_update_statusbar(wifi);
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,3 +610,4 @@ const ServiceManifest wifi_service = {
|
||||
.on_stop = &wifi_service_stop
|
||||
};
|
||||
|
||||
#endif // ESP_TARGET
|
||||
157
tactility-headless/src/services/wifi/wifi_mock.c
Normal file
157
tactility-headless/src/services/wifi/wifi_mock.c
Normal file
@ -0,0 +1,157 @@
|
||||
#include "wifi.h"
|
||||
|
||||
#ifndef ESP_TARGET
|
||||
|
||||
#include "assets.h"
|
||||
#include "check.h"
|
||||
#include "log.h"
|
||||
#include "message_queue.h"
|
||||
#include "mutex.h"
|
||||
#include "pubsub.h"
|
||||
#include "service.h"
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define TAG "wifi"
|
||||
#define WIFI_SCAN_RECORD_LIMIT 16 // default, can be overridden
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
typedef struct {
|
||||
/** @brief Locking mechanism for modifying the Wifi instance */
|
||||
Mutex* mutex;
|
||||
/** @brief The public event bus */
|
||||
PubSub* pubsub;
|
||||
/** @brief The internal message queue */
|
||||
MessageQueue* queue;
|
||||
bool scan_active;
|
||||
bool secure_connection;
|
||||
WifiRadioState radio_state;
|
||||
} Wifi;
|
||||
|
||||
|
||||
static Wifi* wifi_singleton = NULL;
|
||||
|
||||
// Forward declarations
|
||||
static void wifi_lock(Wifi* wifi);
|
||||
static void wifi_unlock(Wifi* wifi);
|
||||
|
||||
// region Alloc
|
||||
|
||||
static Wifi* wifi_alloc() {
|
||||
Wifi* instance = malloc(sizeof(Wifi));
|
||||
instance->mutex = tt_mutex_alloc(MutexTypeRecursive);
|
||||
instance->pubsub = tt_pubsub_alloc();
|
||||
instance->scan_active = false;
|
||||
instance->radio_state = WIFI_RADIO_OFF;
|
||||
instance->secure_connection = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void wifi_free(Wifi* instance) {
|
||||
tt_mutex_free(instance->mutex);
|
||||
tt_pubsub_free(instance->pubsub);
|
||||
tt_message_queue_free(instance->queue);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
// endregion Alloc
|
||||
|
||||
// region Public functions
|
||||
|
||||
PubSub* wifi_get_pubsub() {
|
||||
tt_assert(wifi_singleton);
|
||||
return wifi_singleton->pubsub;
|
||||
}
|
||||
|
||||
WifiRadioState wifi_get_radio_state() {
|
||||
return wifi_singleton->radio_state;
|
||||
}
|
||||
|
||||
void wifi_scan() {
|
||||
tt_assert(wifi_singleton);
|
||||
wifi_singleton->scan_active = false; // TODO: enable and then later disable automatically
|
||||
}
|
||||
|
||||
bool wifi_is_scanning() {
|
||||
tt_assert(wifi_singleton);
|
||||
return wifi_singleton->scan_active;
|
||||
}
|
||||
|
||||
void wifi_connect(const char* ssid, const char _Nullable password[64]) {
|
||||
tt_assert(wifi_singleton);
|
||||
tt_check(strlen(ssid) <= 32);
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void wifi_disconnect() {
|
||||
tt_assert(wifi_singleton);
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void wifi_set_scan_records(uint16_t records) {
|
||||
tt_assert(wifi_singleton);
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void wifi_get_scan_results(WifiApRecord records[], uint16_t limit, uint16_t* result_count) {
|
||||
tt_check(wifi_singleton);
|
||||
tt_check(result_count);
|
||||
|
||||
// TODO: implement
|
||||
*result_count = 0;
|
||||
}
|
||||
|
||||
void wifi_set_enabled(bool enabled) {
|
||||
tt_assert(wifi_singleton != NULL);
|
||||
if (enabled) {
|
||||
wifi_singleton->radio_state = WIFI_RADIO_CONNECTION_ACTIVE;
|
||||
wifi_singleton->secure_connection = true;
|
||||
} else {
|
||||
wifi_singleton->radio_state = WIFI_RADIO_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
bool wifi_is_connection_secure() {
|
||||
return wifi_singleton->secure_connection;
|
||||
}
|
||||
|
||||
int wifi_get_rssi() {
|
||||
// TODO: implement
|
||||
return -10;
|
||||
}
|
||||
|
||||
// endregion Public functions
|
||||
|
||||
static void wifi_lock(Wifi* wifi) {
|
||||
tt_crash("this fails for now");
|
||||
tt_assert(wifi);
|
||||
tt_assert(wifi->mutex);
|
||||
tt_mutex_acquire(wifi->mutex, 100);
|
||||
}
|
||||
|
||||
static void wifi_unlock(Wifi* wifi) {
|
||||
tt_assert(wifi);
|
||||
tt_assert(wifi->mutex);
|
||||
tt_mutex_release(wifi->mutex);
|
||||
}
|
||||
|
||||
|
||||
static void wifi_service_start(TT_UNUSED Service service) {
|
||||
tt_check(wifi_singleton == NULL);
|
||||
wifi_singleton = wifi_alloc();
|
||||
}
|
||||
|
||||
static void wifi_service_stop(TT_UNUSED Service service) {
|
||||
tt_check(wifi_singleton != NULL);
|
||||
|
||||
wifi_free(wifi_singleton);
|
||||
wifi_singleton = NULL;
|
||||
}
|
||||
|
||||
const ServiceManifest wifi_service = {
|
||||
.id = "wifi",
|
||||
.on_start = &wifi_service_start,
|
||||
.on_stop = &wifi_service_stop
|
||||
};
|
||||
|
||||
#endif // ESP_TARGET
|
||||
16
tactility-headless/src/tactility_headless.c
Normal file
16
tactility-headless/src/tactility_headless.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include "tactility_headless.h"
|
||||
#include "hardware_config.h"
|
||||
#include "hardware_i.h"
|
||||
#include "service_registry.h"
|
||||
|
||||
static const HardwareConfig* hardwareConfig = NULL;
|
||||
|
||||
void tt_tactility_headless_init(const HardwareConfig* config, const ServiceManifest* const services[32]) {
|
||||
tt_service_registry_init();
|
||||
tt_hardware_init(config);
|
||||
hardwareConfig = config;
|
||||
}
|
||||
|
||||
const HardwareConfig* tt_get_hardware_config() {
|
||||
return hardwareConfig;
|
||||
}
|
||||
17
tactility-headless/src/tactility_headless.h
Normal file
17
tactility-headless/src/tactility_headless.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "hardware_config.h"
|
||||
#include "service_manifest.h"
|
||||
#include "tactility_headless_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void tt_tactility_headless_init(const HardwareConfig* config, const ServiceManifest* const services[32]);
|
||||
|
||||
const HardwareConfig* tt_get_hardware_config();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
3
tactility-headless/src/tactility_headless_config.h
Normal file
3
tactility-headless/src/tactility_headless_config.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define TT_CONFIG_SERVICES_LIMIT 32
|
||||
@ -3,35 +3,39 @@ set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "src/*.c")
|
||||
file(GLOB_RECURSE HEADERS "src/*.h")
|
||||
|
||||
add_library(tactility OBJECT)
|
||||
target_sources(tactility
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
target_include_directories(tactility
|
||||
PRIVATE src/
|
||||
INTERFACE src/
|
||||
)
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
target_link_libraries(tactility
|
||||
PUBLIC tactility-core
|
||||
PUBLIC idf::lvgl # libs/
|
||||
PUBLIC idf::driver
|
||||
PUBLIC idf::spiffs
|
||||
PUBLIC idf::nvs_flash
|
||||
PUBLIC idf::newlib # for scandir() and related
|
||||
file(GLOB_RECURSE SOURCE_FILES src/*.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "src/"
|
||||
REQUIRES tactility-headless lvgl
|
||||
)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB}
|
||||
PUBLIC lv_screenshot
|
||||
)
|
||||
|
||||
add_definitions(-DESP_PLATFORM)
|
||||
else()
|
||||
file(GLOB_RECURSE SOURCES "src/*.c")
|
||||
file(GLOB_RECURSE HEADERS "src/*.h")
|
||||
|
||||
add_library(tactility OBJECT)
|
||||
target_sources(tactility
|
||||
PRIVATE ${SOURCES}
|
||||
PUBLIC ${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(tactility
|
||||
PRIVATE src/
|
||||
INTERFACE src/
|
||||
)
|
||||
|
||||
add_definitions(-D_Nullable=)
|
||||
add_definitions(-D_Nonnull=)
|
||||
target_link_libraries(tactility
|
||||
PUBLIC tactility-core
|
||||
PUBLIC tactility-headless
|
||||
PUBLIC lvgl
|
||||
PUBLIC freertos_kernel
|
||||
PUBLIC lv_screenshot
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#ifdef ESP_TARGET
|
||||
|
||||
#include "services/loader/loader.h"
|
||||
#include "ui/toolbar.h"
|
||||
#include "thread.h"
|
||||
@ -203,3 +205,5 @@ const AppManifest gpio_app = {
|
||||
.on_show = &app_show,
|
||||
.on_hide = &on_hide
|
||||
};
|
||||
|
||||
#endif // ESP_TARGET
|
||||
@ -1,7 +1,7 @@
|
||||
#include "wifi_manage.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "apps/system/wifi_connect/wifi_connect_bundle.h"
|
||||
#include "apps/wifi_connect/wifi_connect_bundle.h"
|
||||
#include "services/loader/loader.h"
|
||||
#include "tactility_core.h"
|
||||
#include "ui/lvgl_sync.h"
|
||||
@ -1,6 +1,7 @@
|
||||
#include "wifi_manage_view.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "services/statusbar_updater/statusbar_updater.h"
|
||||
#include "services/wifi/wifi.h"
|
||||
#include "ui/style.h"
|
||||
#include "ui/toolbar.h"
|
||||
@ -1,12 +1,8 @@
|
||||
#include "hardware_i.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "lvgl_init_i.h"
|
||||
#include "preferences.h"
|
||||
#include "sdcard_i.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#define TAG "hardware"
|
||||
|
||||
static void init_display_settings(const HardwareConfig* config) {
|
||||
void tt_lvgl_init(const HardwareConfig* config) {
|
||||
SetBacklightDuty set_backlight_duty = config->display.set_backlight_duty;
|
||||
if (set_backlight_duty != NULL) {
|
||||
int32_t backlight_duty = 200;
|
||||
@ -24,21 +20,3 @@ static void init_display_settings(const HardwareConfig* config) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tt_hardware_init(const HardwareConfig* config) {
|
||||
if (config->bootstrap != NULL) {
|
||||
TT_LOG_I(TAG, "Bootstrapping");
|
||||
tt_check(config->bootstrap(), "bootstrap failed");
|
||||
}
|
||||
|
||||
tt_sdcard_init();
|
||||
if (config->sdcard != NULL) {
|
||||
TT_LOG_I(TAG, "Mounting sdcard");
|
||||
tt_sdcard_mount(config->sdcard);
|
||||
}
|
||||
|
||||
tt_check(config->init_lvgl, "lvlg init not set");
|
||||
tt_check(config->init_lvgl(), "lvgl init failed");
|
||||
|
||||
init_display_settings(config);
|
||||
}
|
||||
13
tactility/src/lvgl_init_i.h
Normal file
13
tactility/src/lvgl_init_i.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "hardware_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void tt_lvgl_init(const HardwareConfig* config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,6 +1,7 @@
|
||||
#include "gui_i.h"
|
||||
|
||||
#include "tactility.h"
|
||||
#include "services/loader/loader.h"
|
||||
#include "ui/lvgl_sync.h"
|
||||
#include "ui/lvgl_keypad.h"
|
||||
|
||||
@ -18,6 +19,19 @@ static int32_t gui_main(void*);
|
||||
|
||||
Gui* gui = NULL;
|
||||
|
||||
typedef void (*PubSubCallback)(const void* message, void* context);
|
||||
void gui_loader_callback(const void* message, void* context) {
|
||||
Gui* gui = (Gui*)context;
|
||||
LoaderEvent* event = (LoaderEvent*)message;
|
||||
if (event->type == LoaderEventTypeApplicationShowing) {
|
||||
App* app = event->app_showing.app;
|
||||
AppManifest* app_manifest = tt_app_get_manifest(app);
|
||||
gui_show_app(app, app_manifest->on_show, app_manifest->on_hide);
|
||||
} else if (event->type == LoaderEventTypeApplicationHiding) {
|
||||
gui_hide_app();
|
||||
}
|
||||
}
|
||||
|
||||
Gui* gui_alloc() {
|
||||
Gui* instance = malloc(sizeof(Gui));
|
||||
memset(instance, 0, sizeof(Gui));
|
||||
@ -30,7 +44,7 @@ Gui* gui_alloc() {
|
||||
);
|
||||
instance->mutex = tt_mutex_alloc(MutexTypeRecursive);
|
||||
instance->keyboard = NULL;
|
||||
|
||||
instance->loader_pubsub_subscription = tt_pubsub_subscribe(loader_get_pubsub(), &gui_loader_callback, instance);
|
||||
tt_check(tt_lvgl_lock(1000 / portTICK_PERIOD_MS));
|
||||
instance->keyboard_group = lv_group_create();
|
||||
instance->lvgl_parent = lv_scr_act();
|
||||
|
||||
@ -18,6 +18,7 @@ struct Gui {
|
||||
// Thread and lock
|
||||
Thread* thread;
|
||||
Mutex* mutex;
|
||||
PubSubSubscription* loader_pubsub_subscription;
|
||||
|
||||
// Layers and Canvas
|
||||
lv_obj_t* lvgl_parent;
|
||||
|
||||
@ -17,6 +17,10 @@
|
||||
|
||||
#define TAG "loader"
|
||||
|
||||
typedef struct {
|
||||
LoaderEventType type;
|
||||
} LoaderEventInternal;
|
||||
|
||||
// Forward declarations
|
||||
static int32_t loader_main(void* p);
|
||||
|
||||
@ -25,7 +29,8 @@ static Loader* loader_singleton = NULL;
|
||||
static Loader* loader_alloc() {
|
||||
tt_check(loader_singleton == NULL);
|
||||
loader_singleton = malloc(sizeof(Loader));
|
||||
loader_singleton->pubsub = tt_pubsub_alloc();
|
||||
loader_singleton->pubsub_internal = tt_pubsub_alloc();
|
||||
loader_singleton->pubsub_external = tt_pubsub_alloc();
|
||||
loader_singleton->queue = tt_message_queue_alloc(1, sizeof(LoaderMessage));
|
||||
loader_singleton->thread = tt_thread_alloc_ex(
|
||||
"loader",
|
||||
@ -42,7 +47,8 @@ static Loader* loader_alloc() {
|
||||
static void loader_free() {
|
||||
tt_check(loader_singleton != NULL);
|
||||
tt_thread_free(loader_singleton->thread);
|
||||
tt_pubsub_free(loader_singleton->pubsub);
|
||||
tt_pubsub_free(loader_singleton->pubsub_internal);
|
||||
tt_pubsub_free(loader_singleton->pubsub_external);
|
||||
tt_message_queue_free(loader_singleton->queue);
|
||||
tt_mutex_free(loader_singleton->mutex);
|
||||
free(loader_singleton);
|
||||
@ -104,7 +110,7 @@ PubSub* loader_get_pubsub() {
|
||||
// it's safe to return pubsub without locking
|
||||
// because it's never freed and loader is never exited
|
||||
// also the loader instance cannot be obtained until the pubsub is created
|
||||
return loader_singleton->pubsub;
|
||||
return loader_singleton->pubsub_external;
|
||||
}
|
||||
|
||||
static const char* app_state_to_string(AppState state) {
|
||||
@ -147,15 +153,23 @@ static void app_transition_to_state(App app, AppState state) {
|
||||
tt_app_set_state(app, AppStateStarted);
|
||||
break;
|
||||
case AppStateShowing:
|
||||
gui_show_app(
|
||||
app,
|
||||
manifest->on_show,
|
||||
manifest->on_hide
|
||||
);
|
||||
LoaderEvent event_showing = {
|
||||
.type = LoaderEventTypeApplicationShowing,
|
||||
.app_showing = {
|
||||
.app = app
|
||||
}
|
||||
};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_external, &event_showing);
|
||||
tt_app_set_state(app, AppStateShowing);
|
||||
break;
|
||||
case AppStateHiding:
|
||||
gui_hide_app();
|
||||
LoaderEvent event_hiding = {
|
||||
.type = LoaderEventTypeApplicationHiding,
|
||||
.app_hiding = {
|
||||
.app = app
|
||||
}
|
||||
};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_external, &event_hiding);
|
||||
tt_app_set_state(app, AppStateHiding);
|
||||
break;
|
||||
case AppStateStopped:
|
||||
@ -200,8 +214,16 @@ LoaderStatus loader_do_start_app_with_manifest(
|
||||
|
||||
loader_unlock();
|
||||
|
||||
LoaderEvent event = {.type = LoaderEventTypeApplicationStarted};
|
||||
tt_pubsub_publish(loader_singleton->pubsub, &event);
|
||||
LoaderEventInternal event_internal = {.type = LoaderEventTypeApplicationStarted};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_internal, &event_internal);
|
||||
|
||||
LoaderEvent event_external = {
|
||||
.type = LoaderEventTypeApplicationStarted,
|
||||
.app_started = {
|
||||
.app = app
|
||||
}
|
||||
};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_external, &event_external);
|
||||
|
||||
return LoaderStatusOk;
|
||||
}
|
||||
@ -240,6 +262,7 @@ static void loader_do_stop_app() {
|
||||
|
||||
// Stop current app
|
||||
App app_to_stop = loader_singleton->app_stack[current_app_index];
|
||||
AppManifest* manifest = tt_app_get_manifest(app_to_stop);
|
||||
app_transition_to_state(app_to_stop, AppStateHiding);
|
||||
app_transition_to_state(app_to_stop, AppStateStopped);
|
||||
|
||||
@ -258,8 +281,16 @@ static void loader_do_stop_app() {
|
||||
|
||||
loader_unlock();
|
||||
|
||||
LoaderEvent event = {.type = LoaderEventTypeApplicationStopped};
|
||||
tt_pubsub_publish(loader_singleton->pubsub, &event);
|
||||
LoaderEventInternal event_internal = {.type = LoaderEventTypeApplicationStopped};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_internal, &event_internal);
|
||||
|
||||
LoaderEvent event_external = {
|
||||
.type = LoaderEventTypeApplicationStopped,
|
||||
.app_stopped = {
|
||||
.manifest = manifest
|
||||
}
|
||||
};
|
||||
tt_pubsub_publish(loader_singleton->pubsub_external, &event_external);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -21,15 +21,39 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
LoaderEventTypeApplicationStarted,
|
||||
LoaderEventTypeApplicationShowing,
|
||||
LoaderEventTypeApplicationHiding,
|
||||
LoaderEventTypeApplicationStopped
|
||||
} LoaderEventType;
|
||||
|
||||
typedef struct {
|
||||
App* app;
|
||||
} LoaderEventAppStarted;
|
||||
|
||||
typedef struct {
|
||||
App* app;
|
||||
} LoaderEventAppShowing;
|
||||
|
||||
typedef struct {
|
||||
App* app;
|
||||
} LoaderEventAppHiding;
|
||||
|
||||
typedef struct {
|
||||
AppManifest* manifest;
|
||||
} LoaderEventAppStopped;
|
||||
|
||||
typedef struct {
|
||||
LoaderEventType type;
|
||||
union {
|
||||
LoaderEventAppStarted app_started;
|
||||
LoaderEventAppShowing app_showing;
|
||||
LoaderEventAppHiding app_hiding;
|
||||
LoaderEventAppStopped app_stopped;
|
||||
};
|
||||
} LoaderEvent;
|
||||
|
||||
/**
|
||||
* @brief Close any running app, then start new one. Blocking.
|
||||
* @brief Start an app
|
||||
* @param[in] id application name or id
|
||||
* @param[in] blocking application arguments
|
||||
* @param[in] bundle optional bundle. Ownership is transferred to Loader.
|
||||
@ -37,13 +61,15 @@ typedef struct {
|
||||
*/
|
||||
LoaderStatus loader_start_app(const char* id, bool blocking, Bundle* _Nullable bundle);
|
||||
|
||||
/**
|
||||
* @brief Stop the currently showing app. Show the previous app if any app was still running.
|
||||
*/
|
||||
void loader_stop_app();
|
||||
|
||||
App _Nullable loader_get_current_app();
|
||||
|
||||
/**
|
||||
* @brief Get loader pubsub
|
||||
* @return PubSub*
|
||||
* @brief PubSub for LoaderEvent
|
||||
*/
|
||||
PubSub* loader_get_pubsub();
|
||||
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
|
||||
struct Loader {
|
||||
Thread* thread;
|
||||
PubSub* pubsub;
|
||||
PubSub* pubsub_internal;
|
||||
PubSub* pubsub_external;
|
||||
MessageQueue* queue;
|
||||
Mutex* mutex;
|
||||
int8_t app_stack_index;
|
||||
|
||||
160
tactility/src/services/statusbar_updater/statusbar_updater.c
Normal file
160
tactility/src/services/statusbar_updater/statusbar_updater.c
Normal file
@ -0,0 +1,160 @@
|
||||
#include "mutex.h"
|
||||
#include "service.h"
|
||||
#include "ui/statusbar.h"
|
||||
|
||||
#define TAG "wifi_statusbar_service"
|
||||
|
||||
#include "assets.h"
|
||||
#include "sdcard.h"
|
||||
#include "services/wifi/wifi.h"
|
||||
|
||||
typedef struct {
|
||||
Mutex* mutex;
|
||||
Thread* thread;
|
||||
bool service_interrupted;
|
||||
int8_t wifi_icon_id;
|
||||
const char* wifi_last_icon;
|
||||
int8_t sdcard_icon_id;
|
||||
const char* sdcard_last_icon;
|
||||
} ServiceData;
|
||||
|
||||
const char* wifi_get_status_icon_for_rssi(int rssi, bool secured) {
|
||||
if (rssi > 0) {
|
||||
return TT_ASSETS_ICON_WIFI_CONNECTION_ISSUE;
|
||||
} else if (rssi > -67) {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_4_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_4;
|
||||
} else if (rssi > -70) {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_3_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_3;
|
||||
} else if (rssi > -80) {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_2_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_2;
|
||||
} else {
|
||||
return secured ? TT_ASSETS_ICON_WIFI_SIGNAL_1_LOCKED : TT_ASSETS_ICON_WIFI_SIGNAL_1;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* wifi_get_status_icon(WifiRadioState state, bool secure) {
|
||||
int rssi;
|
||||
switch (state) {
|
||||
case WIFI_RADIO_ON_PENDING:
|
||||
case WIFI_RADIO_ON:
|
||||
case WIFI_RADIO_OFF_PENDING:
|
||||
case WIFI_RADIO_OFF:
|
||||
return TT_ASSETS_ICON_WIFI_OFF;
|
||||
case WIFI_RADIO_CONNECTION_PENDING:
|
||||
return TT_ASSETS_ICON_WIFI_FIND;
|
||||
case WIFI_RADIO_CONNECTION_ACTIVE:
|
||||
rssi = wifi_get_rssi();
|
||||
return wifi_get_status_icon_for_rssi(rssi, secure);
|
||||
default:
|
||||
tt_crash_implementation("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
static _Nullable const char* sdcard_get_status_icon(SdcardState state) {
|
||||
switch (state) {
|
||||
case SdcardStateMounted:
|
||||
return TT_ASSETS_ICON_SDCARD;
|
||||
case SdcardStateError:
|
||||
case SdcardStateUnmounted:
|
||||
return TT_ASSETS_ICON_SDCARD_ALERT;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_wifi_icon(ServiceData* data) {
|
||||
WifiRadioState radio_state = wifi_get_radio_state();
|
||||
bool is_secure = wifi_is_connection_secure();
|
||||
const char* desired_icon = wifi_get_status_icon(radio_state, is_secure);
|
||||
if (data->wifi_last_icon != desired_icon) {
|
||||
tt_statusbar_icon_set_image(data->wifi_icon_id, desired_icon);
|
||||
data->wifi_last_icon = desired_icon;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_sdcard_icon(ServiceData* data) {
|
||||
SdcardState state = tt_sdcard_get_state();
|
||||
const char* desired_icon = sdcard_get_status_icon(state);
|
||||
if (data->sdcard_last_icon != desired_icon) {
|
||||
tt_statusbar_icon_set_image(data->sdcard_icon_id, desired_icon);
|
||||
tt_statusbar_icon_set_visibility(data->sdcard_icon_id, desired_icon != NULL);
|
||||
data->sdcard_last_icon = desired_icon;
|
||||
}
|
||||
}
|
||||
|
||||
static ServiceData* service_data_alloc() {
|
||||
ServiceData* data = malloc(sizeof(ServiceData));
|
||||
*data = (ServiceData) {
|
||||
.mutex = tt_mutex_alloc(MutexTypeNormal),
|
||||
.thread = tt_thread_alloc(),
|
||||
.service_interrupted = false,
|
||||
.wifi_icon_id = tt_statusbar_icon_add(NULL),
|
||||
.wifi_last_icon = NULL,
|
||||
.sdcard_icon_id = tt_statusbar_icon_add(NULL),
|
||||
.sdcard_last_icon = NULL,
|
||||
};
|
||||
|
||||
tt_statusbar_icon_set_visibility(data->wifi_icon_id, true);
|
||||
update_wifi_icon(data);
|
||||
update_sdcard_icon(data); // also updates visibility
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void service_data_free(ServiceData* data) {
|
||||
tt_mutex_free(data->mutex);
|
||||
tt_thread_free(data->thread);
|
||||
tt_statusbar_icon_remove(data->wifi_icon_id);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void service_data_lock(ServiceData* data) {
|
||||
tt_check(tt_mutex_acquire(data->mutex, TtWaitForever) == TtStatusOk);
|
||||
}
|
||||
|
||||
static void service_data_unlock(ServiceData* data) {
|
||||
tt_check(tt_mutex_release(data->mutex) == TtStatusOk);
|
||||
}
|
||||
|
||||
int32_t service_main(TT_UNUSED void* parameter) {
|
||||
TT_LOG_I(TAG, "Started main loop");
|
||||
ServiceData* data = (ServiceData*)parameter;
|
||||
tt_check(data != NULL);
|
||||
while (!data->service_interrupted) {
|
||||
update_wifi_icon(data);
|
||||
update_sdcard_icon(data);
|
||||
tt_delay_ms(1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void on_start(Service service) {
|
||||
ServiceData* data = service_data_alloc();
|
||||
|
||||
tt_service_set_data(service, data);
|
||||
|
||||
tt_thread_set_callback(data->thread, service_main);
|
||||
tt_thread_set_current_priority(ThreadPriorityLow);
|
||||
tt_thread_set_stack_size(data->thread, 2048); // 2048 was the minimum when last tested
|
||||
tt_thread_set_context(data->thread, data);
|
||||
tt_thread_start(data->thread);
|
||||
}
|
||||
|
||||
static void on_stop(Service service) {
|
||||
ServiceData* data = tt_service_get_data(service);
|
||||
|
||||
// Stop thread
|
||||
service_data_lock(data);
|
||||
data->service_interrupted = true;
|
||||
service_data_unlock(data);
|
||||
tt_mutex_release(data->mutex);
|
||||
tt_thread_join(data->thread);
|
||||
|
||||
service_data_free(data);
|
||||
}
|
||||
|
||||
const ServiceManifest statusbar_updater_service = {
|
||||
.id = "statusbar_updater",
|
||||
.on_start = &on_start,
|
||||
.on_stop = &on_stop
|
||||
};
|
||||
19
tactility/src/services/statusbar_updater/statusbar_updater.h
Normal file
19
tactility/src/services/statusbar_updater/statusbar_updater.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return the relevant icon asset from assets.h for the given inputs
|
||||
* @param rssi the rssi value
|
||||
* @param secured whether the access point is a secured one (as in: not an open one)
|
||||
* @return
|
||||
*/
|
||||
const char* wifi_get_status_icon_for_rssi(int rssi, bool secured);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@ -1,9 +1,11 @@
|
||||
#include "tactility.h"
|
||||
|
||||
#include "app_manifest_registry.h"
|
||||
#include "hardware_i.h"
|
||||
#include "esp_init.h"
|
||||
#include "lvgl_init_i.h"
|
||||
#include "service_registry.h"
|
||||
#include "services/loader/loader.h"
|
||||
#include "tactility_headless.h"
|
||||
|
||||
#define TAG "tactility"
|
||||
|
||||
@ -15,14 +17,18 @@ extern const ServiceManifest gui_service;
|
||||
extern const ServiceManifest loader_service;
|
||||
extern const ServiceManifest screenshot_service;
|
||||
extern const ServiceManifest sdcard_service;
|
||||
extern const ServiceManifest wifi_service;
|
||||
extern const ServiceManifest statusbar_updater_service;
|
||||
|
||||
static const ServiceManifest* const system_services[] = {
|
||||
&gui_service,
|
||||
&loader_service, // depends on gui service
|
||||
&loader_service,
|
||||
&gui_service, // depends on loader service
|
||||
#ifndef ESP_PLATFORM // Screenshots don't work yet on ESP32
|
||||
&screenshot_service,
|
||||
#endif
|
||||
&sdcard_service
|
||||
&sdcard_service,
|
||||
&wifi_service,
|
||||
&statusbar_updater_service
|
||||
};
|
||||
|
||||
// endregion
|
||||
@ -32,19 +38,28 @@ static const ServiceManifest* const system_services[] = {
|
||||
extern const AppManifest desktop_app;
|
||||
extern const AppManifest display_app;
|
||||
extern const AppManifest files_app;
|
||||
extern const AppManifest screenshot_app;
|
||||
extern const AppManifest settings_app;
|
||||
extern const AppManifest system_info_app;
|
||||
extern const AppManifest wifi_connect_app;
|
||||
extern const AppManifest wifi_manage_app;
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
extern const AppManifest screenshot_app;
|
||||
extern const AppManifest gpio_app;
|
||||
#endif
|
||||
|
||||
static const AppManifest* const system_apps[] = {
|
||||
&desktop_app,
|
||||
&display_app,
|
||||
&files_app,
|
||||
#ifndef ESP_PLATFORM // Screenshots don't work yet on ESP32
|
||||
&settings_app,
|
||||
&system_info_app,
|
||||
&wifi_connect_app,
|
||||
&wifi_manage_app,
|
||||
#ifdef ESP_PLATFORM // Screenshots don't work yet on ESP32
|
||||
&gpio_app,
|
||||
&screenshot_app,
|
||||
#endif
|
||||
&settings_app,
|
||||
&system_info_app
|
||||
};
|
||||
|
||||
// endregion
|
||||
@ -96,13 +111,18 @@ static void register_and_start_user_services(const ServiceManifest* const servic
|
||||
void tt_init(const Config* config) {
|
||||
TT_LOG_I(TAG, "tt_init started");
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
tt_esp_init();
|
||||
#endif
|
||||
|
||||
// Assign early so starting services can use it
|
||||
config_instance = config;
|
||||
|
||||
tt_service_registry_init();
|
||||
tt_app_manifest_registry_init();
|
||||
tt_tactility_headless_init(config->hardware, config->services);
|
||||
|
||||
tt_hardware_init(config->hardware);
|
||||
tt_lvgl_init(config->hardware);
|
||||
|
||||
tt_app_manifest_registry_init();
|
||||
|
||||
// Note: the order of starting apps and services is critical!
|
||||
// System services are registered first so the apps below can use them
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define TT_CONFIG_APPS_LIMIT 32
|
||||
#define TT_CONFIG_SERVICES_LIMIT 32
|
||||
#include "tactility_headless_config.h"
|
||||
|
||||
#define TT_CONFIG_APPS_LIMIT 32
|
||||
#define TT_CONFIG_FORCE_ONSCREEN_KEYBOARD false
|
||||
@ -25,7 +25,7 @@ typedef struct {
|
||||
|
||||
static StatusbarData statusbar_data = {
|
||||
.mutex = NULL,
|
||||
.icons = {0}
|
||||
.icons = {}
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user