Compare commits
2 Commits
f5fae825d6
...
a008106d47
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a008106d47 | ||
|
|
7bd230f591 |
@@ -1,5 +1,20 @@
|
|||||||
set(requires esp-tls spi_flash nvs_flash esp_event esp_netif esp_http_client esp_http_server esp_wifi esp_psram esp_lvgl_port)
|
set(requires esp-tls spi_flash nvs_flash esp_event esp_netif esp_http_client esp_http_server esp_wifi esp_psram esp_lvgl_port)
|
||||||
file(GLOB_RECURSE SRCS "main.cpp" "*.cpp" "*.c" "ui/**/*.cpp" "ui/**/*.c" "external/**/*.cpp" "external/**/*.c")
|
|
||||||
|
# Start the source list with the known root source
|
||||||
|
set(SRCS "${CMAKE_CURRENT_LIST_DIR}/main.cpp")
|
||||||
|
# Delegate source collection to per-directory CMakeLists (non-recursive)
|
||||||
|
set(SUBDIRS "display" "external" "ui" "io" "network" "info" "common")
|
||||||
|
foreach(dir IN LISTS SUBDIRS)
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/${dir}/CMakeLists.txt")
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/${dir}/CMakeLists.txt")
|
||||||
|
else()
|
||||||
|
file(GLOB DIR_SRCS "${CMAKE_CURRENT_LIST_DIR}/${dir}/*.c" "${CMAKE_CURRENT_LIST_DIR}/${dir}/*.cpp")
|
||||||
|
if(DIR_SRCS)
|
||||||
|
list(APPEND SRCS ${DIR_SRCS})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Path to the source JSON in this component
|
# Path to the source JSON in this component
|
||||||
|
|||||||
1
main/common/CMakeLists.txt
Normal file
1
main/common/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# common/ currently contains headers; no sources to add by default
|
||||||
5
main/display/CMakeLists.txt
Normal file
5
main/display/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/eink_display_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/epd_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/lvgl_handler.cpp"
|
||||||
|
)
|
||||||
6
main/external/CMakeLists.txt
vendored
Normal file
6
main/external/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/mtr/mtr.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/mtr/station_info.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/mtr/line_info.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/mtr/arrival.cpp"
|
||||||
|
)
|
||||||
3
main/info/CMakeLists.txt
Normal file
3
main/info/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/info.cpp"
|
||||||
|
)
|
||||||
4
main/io/CMakeLists.txt
Normal file
4
main/io/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/fs_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/nvs_handler.cpp"
|
||||||
|
)
|
||||||
7
main/network/CMakeLists.txt
Normal file
7
main/network/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/http_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/wifi_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/web_server_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/udp_client.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/network.cpp"
|
||||||
|
)
|
||||||
14
main/ui/CMakeLists.txt
Normal file
14
main/ui/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ui_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/root_layout.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/interaction_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/events.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/apps/registry.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/widgets/textarea.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/widgets/button.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apps control: include apps/CMakeLists.txt which selects which apps to add
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/apps/CMakeLists.txt")
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/apps/CMakeLists.txt")
|
||||||
|
endif()
|
||||||
19
main/ui/apps/CMakeLists.txt
Normal file
19
main/ui/apps/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Control which apps are included in the build.
|
||||||
|
# Override `ENABLED_APPS` from the top-level CMake command line to change apps.
|
||||||
|
if(NOT DEFINED ENABLED_APPS)
|
||||||
|
set(ENABLED_APPS "iotdis")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Enabled apps: ${ENABLED_APPS}")
|
||||||
|
|
||||||
|
foreach(app IN LISTS ENABLED_APPS)
|
||||||
|
set(APP_DIR "${CMAKE_CURRENT_LIST_DIR}/${app}")
|
||||||
|
if(EXISTS "${APP_DIR}/CMakeLists.txt")
|
||||||
|
include("${APP_DIR}/CMakeLists.txt")
|
||||||
|
else()
|
||||||
|
message(WARNING "App '${app}' has no CMakeLists.txt — attempting to add any sources directly")
|
||||||
|
file(GLOB APP_SRCS "${APP_DIR}/*.c" "${APP_DIR}/*.cpp" "${APP_DIR}/*/*.c" "${APP_DIR}/*/*.cpp")
|
||||||
|
if(APP_SRCS)
|
||||||
|
list(APPEND SRCS ${APP_SRCS})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
12
main/ui/apps/iotdis/CMakeLists.txt
Normal file
12
main/ui/apps/iotdis/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Explicit list of iotdis app sources
|
||||||
|
list(APPEND SRCS
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/web/web_handlers.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/descriptor.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/settings/settings_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/app.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/bridge/bridge.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ui/settings_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ui/settings.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ui/main_handler.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/ui/main.cpp"
|
||||||
|
)
|
||||||
@@ -135,19 +135,9 @@ void IotDisBridge::poll_task_(void* param) {
|
|||||||
ESP_LOGI(TAG, "Polling task started");
|
ESP_LOGI(TAG, "Polling task started");
|
||||||
|
|
||||||
while (!bridge->stop_polling_) {
|
while (!bridge->stop_polling_) {
|
||||||
ESP_LOGI(TAG, "Polling for status update...");
|
|
||||||
bridge->poll_status_();
|
bridge->poll_status_();
|
||||||
ESP_LOGI(TAG, "poll_status_() returned");
|
// Yield to allow other tasks to run
|
||||||
|
|
||||||
// Yield to allow display updates to complete
|
|
||||||
taskYIELD();
|
taskYIELD();
|
||||||
|
|
||||||
// Use longer interval if in error state
|
|
||||||
int interval = (bridge->consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR)
|
|
||||||
? ERROR_POLL_INTERVAL_MS
|
|
||||||
: POLL_INTERVAL_MS;
|
|
||||||
ESP_LOGI(TAG, "Next poll in %d ms", interval);
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(interval));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Polling task stopped");
|
ESP_LOGI(TAG, "Polling task stopped");
|
||||||
@@ -161,44 +151,49 @@ void IotDisBridge::poll_status_() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First check for any unsolicited push messages (non-blocking)
|
// Continuously listen for messages (blocking with timeout)
|
||||||
std::string push_message;
|
// Use longer timeout if in error state
|
||||||
esp_err_t err = udp_client_.receive_response(push_message, 0); // 0 = non-blocking
|
int listen_timeout = (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR)
|
||||||
|
? ERROR_POLL_INTERVAL_MS
|
||||||
|
: POLL_INTERVAL_MS;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Listening for messages (timeout: %dms)", listen_timeout);
|
||||||
|
std::string message;
|
||||||
|
esp_err_t err = udp_client_.receive_response(message, listen_timeout);
|
||||||
|
|
||||||
|
if (err == ESP_OK && !message.empty()) {
|
||||||
|
// Received a message (either push update or status response)
|
||||||
|
ESP_LOGI(TAG, "Received message: %s", message.c_str());
|
||||||
|
|
||||||
if (err == ESP_OK && !push_message.empty()) {
|
|
||||||
// Received push update from remote
|
|
||||||
ESP_LOGI(TAG, "Received push update: %s", push_message.c_str());
|
|
||||||
StatusUpdateEventData event_data {
|
StatusUpdateEventData event_data {
|
||||||
.state = StatusUpdateEventData::VoiceState::UNKNOWN
|
.state = StatusUpdateEventData::VoiceState::UNKNOWN
|
||||||
};
|
};
|
||||||
|
|
||||||
if (push_message == MUTED_RESPONSE) {
|
if (message == MUTED_RESPONSE) {
|
||||||
event_data.state = StatusUpdateEventData::VoiceState::MUTED;
|
event_data.state = StatusUpdateEventData::VoiceState::MUTED;
|
||||||
} else if (push_message == UNMUTED_RESPONSE) {
|
} else if (message == UNMUTED_RESPONSE) {
|
||||||
event_data.state = StatusUpdateEventData::VoiceState::UNMUTED;
|
event_data.state = StatusUpdateEventData::VoiceState::UNMUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset failure counter on successful push update
|
// Reset failure counter on successful message
|
||||||
if (event_data.state != StatusUpdateEventData::VoiceState::UNKNOWN) {
|
if (event_data.state != StatusUpdateEventData::VoiceState::UNKNOWN) {
|
||||||
consecutive_failures_ = 0;
|
consecutive_failures_ = 0;
|
||||||
}
|
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Invoking status update callback with state: %d", event_data.state);
|
||||||
if (on_status_update_callback_) {
|
if (on_status_update_callback_) {
|
||||||
on_status_update_callback_(event_data, status_event_user_data_);
|
on_status_update_callback_(event_data, status_event_user_data_);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Got push update, skip polling
|
ESP_LOGW(TAG, "Received unknown message: %s", message.c_str());
|
||||||
if (event_data.state != StatusUpdateEventData::VoiceState::UNKNOWN) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
} else if (err == ESP_ERR_TIMEOUT) {
|
||||||
|
// Timeout - send STATUS command to verify connection is still alive
|
||||||
// Send STATUS command for polling
|
ESP_LOGI(TAG, "Listen timeout, sending STATUS command to verify connection");
|
||||||
ESP_LOGI(TAG, "Sending STATUS command for polling");
|
|
||||||
err = udp_client_.send_command(STATUS_COMMAND);
|
err = udp_client_.send_command(STATUS_COMMAND);
|
||||||
|
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
consecutive_failures_++;
|
consecutive_failures_++;
|
||||||
ESP_LOGW(TAG, "Failed to send STATUS command for polling. Consecutive failures: %d",
|
ESP_LOGW(TAG, "Failed to send STATUS command. Consecutive failures: %d",
|
||||||
consecutive_failures_);
|
consecutive_failures_);
|
||||||
|
|
||||||
if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) {
|
if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) {
|
||||||
@@ -209,41 +204,14 @@ void IotDisBridge::poll_status_() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for response to STATUS command
|
|
||||||
ESP_LOGI(TAG, "Waiting for response to STATUS command (timeout: %dms)", RESPONSE_TIMEOUT_MS);
|
|
||||||
std::string response;
|
|
||||||
err = udp_client_.receive_response(response, RESPONSE_TIMEOUT_MS);
|
|
||||||
ESP_LOGI(TAG, "Received response from STATUS command: err=%d", err);
|
|
||||||
|
|
||||||
if (err == ESP_OK) {
|
|
||||||
// Success - reset failure counter
|
|
||||||
consecutive_failures_ = 0;
|
|
||||||
ESP_LOGI(TAG, "STATUS response: %s", response.c_str());
|
|
||||||
|
|
||||||
StatusUpdateEventData event_data {
|
|
||||||
.state = StatusUpdateEventData::VoiceState::UNKNOWN
|
|
||||||
};
|
|
||||||
if (response == MUTED_RESPONSE) {
|
|
||||||
event_data.state = StatusUpdateEventData::VoiceState::MUTED;
|
|
||||||
} else if (response == UNMUTED_RESPONSE) {
|
|
||||||
event_data.state = StatusUpdateEventData::VoiceState::UNMUTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Invoking status update callback with state: %d", event_data.state);
|
|
||||||
if (on_status_update_callback_) {
|
|
||||||
on_status_update_callback_(event_data, status_event_user_data_);
|
|
||||||
ESP_LOGI(TAG, "Status update callback returned");
|
|
||||||
}
|
}
|
||||||
|
// The response to STATUS command will be received in the next iteration
|
||||||
} else {
|
} else {
|
||||||
// Timeout or error
|
// Error receiving
|
||||||
consecutive_failures_++;
|
consecutive_failures_++;
|
||||||
ESP_LOGW(TAG, "No response to STATUS (failures: %d, error: %d)", consecutive_failures_, err);
|
ESP_LOGW(TAG, "Error receiving message: %d (failures: %d)", err, consecutive_failures_);
|
||||||
|
|
||||||
if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) {
|
if (consecutive_failures_ >= MAX_FAILURES_BEFORE_ERROR) {
|
||||||
ESP_LOGW(TAG, "Max failures reached, sending ERROR state");
|
|
||||||
if (on_status_update_callback_) {
|
if (on_status_update_callback_) {
|
||||||
on_status_update_callback_(
|
on_status_update_callback_(
|
||||||
StatusUpdateEventData { .state = StatusUpdateEventData::VoiceState::ERROR },
|
StatusUpdateEventData { .state = StatusUpdateEventData::VoiceState::ERROR },
|
||||||
@@ -252,6 +220,4 @@ void IotDisBridge::poll_status_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "poll_status_ complete");
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
#include "ui/apps/iotdis/ui/main.h"
|
#include "ui/apps/iotdis/ui/main.h"
|
||||||
#include "ui/apps/iotdis/app.h"
|
#include "ui/apps/iotdis/app.h"
|
||||||
#include "ui/interaction_handler.h"
|
#include "ui/interaction_handler.h"
|
||||||
|
#include "ui/widgets/button.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
|
||||||
#define LVGL_LOCK_TIMEOUT 2000 // milliseconds
|
#define LVGL_LOCK_TIMEOUT 4000 // milliseconds
|
||||||
static const char* TAG = "MainUI";
|
static const char* TAG = "MainUI";
|
||||||
|
|
||||||
MainUI::~MainUI() {
|
MainUI::~MainUI() {
|
||||||
@@ -82,11 +83,8 @@ void MainUI::create_ui_(lv_obj_t* parent) {
|
|||||||
lv_label_set_text(status_text_label_, "Unknown Status");
|
lv_label_set_text(status_text_label_, "Unknown Status");
|
||||||
|
|
||||||
// Mute button
|
// Mute button
|
||||||
mute_button_ = lv_btn_create(center_container);
|
mute_button_ = button_create(center_container);
|
||||||
lv_obj_set_size(mute_button_, 200, 60);
|
lv_obj_set_size(mute_button_, 200, 60);
|
||||||
lv_obj_set_style_bg_color(mute_button_, lv_color_white(), 0);
|
|
||||||
lv_obj_set_style_border_color(mute_button_, lv_color_black(), 0);
|
|
||||||
lv_obj_set_style_border_width(mute_button_, 2, 0);
|
|
||||||
lv_obj_t* mute_label = lv_label_create(mute_button_);
|
lv_obj_t* mute_label = lv_label_create(mute_button_);
|
||||||
lv_label_set_text(mute_label, "MUTE");
|
lv_label_set_text(mute_label, "MUTE");
|
||||||
lv_obj_center(mute_label);
|
lv_obj_center(mute_label);
|
||||||
@@ -107,11 +105,8 @@ void MainUI::create_ui_(lv_obj_t* parent) {
|
|||||||
lv_obj_set_style_text_color(config_prompt_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(config_prompt_, lv_color_black(), 0);
|
||||||
|
|
||||||
// Settings button (right side)
|
// Settings button (right side)
|
||||||
settings_button_ = lv_btn_create(bottom_container);
|
settings_button_ = button_create(bottom_container);
|
||||||
lv_obj_set_size(settings_button_, 60, 60);
|
lv_obj_set_size(settings_button_, 60, 60);
|
||||||
lv_obj_set_style_bg_color(settings_button_, lv_color_white(), 0);
|
|
||||||
lv_obj_set_style_border_color(settings_button_, lv_color_black(), 0);
|
|
||||||
lv_obj_set_style_border_width(settings_button_, 2, 0);
|
|
||||||
|
|
||||||
lv_obj_t* settings_icon = lv_label_create(settings_button_);
|
lv_obj_t* settings_icon = lv_label_create(settings_button_);
|
||||||
lv_label_set_text(settings_icon, LV_SYMBOL_SETTINGS);
|
lv_label_set_text(settings_icon, LV_SYMBOL_SETTINGS);
|
||||||
|
|||||||
12
main/ui/widgets/button.cpp
Normal file
12
main/ui/widgets/button.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "ui/widgets/button.h"
|
||||||
|
|
||||||
|
lv_obj_t* button_create(lv_obj_t* parent) {
|
||||||
|
lv_obj_t* button = lv_button_create(parent);
|
||||||
|
lv_obj_set_style_bg_color(button, lv_color_white(), 0);
|
||||||
|
lv_obj_set_style_border_color(button, lv_color_black(), 0);
|
||||||
|
lv_obj_set_style_border_width(button, 2, 0);
|
||||||
|
lv_anim_delete(button, nullptr);
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
4
main/ui/widgets/button.h
Normal file
4
main/ui/widgets/button.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
lv_obj_t* button_create(lv_obj_t* parent);
|
||||||
4
main/ui/widgets/widgets.h
Normal file
4
main/ui/widgets/widgets.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "main/ui/widgets/button.h"
|
||||||
|
#include "main/ui/widgets/textarea.h"
|
||||||
Reference in New Issue
Block a user