Compare commits

..

2 Commits

Author SHA1 Message Date
Ken Van Hoeylandt
7e6b6d5463 Added new calls 2025-06-20 00:18:11 +02:00
Ken Van Hoeylandt
e66033c1c6 Development service work in progress 2025-06-19 23:58:58 +02:00
6 changed files with 292 additions and 3 deletions

View File

@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (DEFINED ENV{ESP_IDF_VERSION})
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
list(APPEND REQUIRES_LIST TactilityCore lvgl driver elf_loader lv_screenshot QRCode esp_lvgl_port minmea esp_wifi nvs_flash spiffs vfs fatfs lwip)
list(APPEND REQUIRES_LIST TactilityCore lvgl driver elf_loader lv_screenshot QRCode esp_lvgl_port minmea esp_wifi nvs_flash spiffs vfs fatfs lwip esp_http_server)
if ("${IDF_TARGET}" STREQUAL "esp32s3")
list(APPEND REQUIRES_LIST esp_tinyusb)
endif ()

View File

@ -0,0 +1,78 @@
#pragma once
#ifdef ESP_PLATFORM
#include "Tactility/service/Service.h"
#include <Tactility/Mutex.h>
#include <esp_event.h>
#include <esp_http_server.h>
#include <Tactility/kernel/SystemEvents.h>
namespace tt::service::development {
class Development final : public Service {
Mutex mutex = Mutex(Mutex::Type::Recursive);
httpd_handle_t server = nullptr;
bool enabled = false;
kernel::SystemEventSubscription networkConnectEventSubscription = 0;
kernel::SystemEventSubscription networkDisconnectEventSubscription = 0;
std::string deviceResponse;
httpd_uri_t handleGetInfoEndpoint = {
.uri = "/info",
.method = HTTP_GET,
.handler = handleGetInfo,
.user_ctx = this
};
httpd_uri_t appRunEndpoint = {
.uri = "/app/run",
.method = HTTP_POST,
.handler = handleAppRun,
.user_ctx = this
};
httpd_uri_t appInstallEndpoint = {
.uri = "/app/install",
.method = HTTP_POST,
.handler = handleAppInstall,
.user_ctx = this
};
void onNetworkConnected();
void onNetworkDisconnected();
void startServer();
void stopServer();
static esp_err_t handleGetInfo(httpd_req_t* request);
static esp_err_t handleAppRun(httpd_req_t* request);
static esp_err_t handleAppInstall(httpd_req_t* request);
public:
// region Overrides
void onStart(ServiceContext& service) override;
void onStop(ServiceContext& service) override;
// endregion Overrides
// region Internal API
void setEnabled(bool enabled);
bool isEnabled() const;
bool isStarted() const;
// region Internal API
};
std::shared_ptr<Development> findService();
}
#endif // ESP_PLATFORM

View File

@ -20,6 +20,7 @@ namespace service::gps { extern const ServiceManifest manifest; }
namespace service::wifi { extern const ServiceManifest manifest; }
namespace service::sdcard { extern const ServiceManifest manifest; }
#ifdef ESP_PLATFORM
namespace service::development { extern const ServiceManifest manifest; }
namespace service::espnow { extern const ServiceManifest manifest; }
#endif
@ -33,6 +34,7 @@ static void registerAndStartSystemServices() {
addService(service::sdcard::manifest);
addService(service::wifi::manifest);
#ifdef ESP_PLATFORM
addService(service::development::manifest);
addService(service::espnow::manifest);
#endif
}

View File

@ -118,7 +118,7 @@ static void statusbar_pubsub_event(TT_UNUSED const void* message, void* obj) {
}
}
static void onNetworkConnected(TT_UNUSED kernel::SystemEvent event) {
static void onTimeChanged(TT_UNUSED kernel::SystemEvent event) {
if (statusbar_data.mutex.lock(100 / portTICK_PERIOD_MS)) {
statusbar_data.time_update_timer->stop();
statusbar_data.time_update_timer->start(5);
@ -139,7 +139,7 @@ static void statusbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
statusbar_data.time_update_timer->start(50 / portTICK_PERIOD_MS);
statusbar_data.systemEventSubscription = kernel::subscribeSystemEvent(
kernel::SystemEvent::Time,
onNetworkConnected
onTimeChanged
);
}
}

View File

@ -0,0 +1,206 @@
#ifdef ESP_PLATFORM
#include "Tactility/service/development/Development.h"
#include "Tactility/TactilityHeadless.h"
#include "Tactility/service/ServiceManifest.h"
#include "Tactility/service/ServiceRegistry.h"
#include <cstring>
#include <esp_wifi.h>
#include <sstream>
namespace tt::service::development {
extern const ServiceManifest manifest;
constexpr const char* TAG = "DevService";
static char* rest_read_buffer(httpd_req_t* request) {
static char buffer[1024];
int contentLength = request->content_len;
int currentLength = 0;
int received = 0;
if (contentLength >= 1024) {
// Respond with 500 Internal Server Error
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
return NULL;
}
while (currentLength < contentLength) {
received = httpd_req_recv(request, buffer + currentLength, contentLength);
if (received <= 0) {
// Respond with 500 Internal Server Error
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to post control value");
return NULL;
}
currentLength += received;
}
buffer[contentLength] = '\0';
return buffer;
}
void Development::onStart(ServiceContext& service) {
auto lock = mutex.asScopedLock();
lock.lock();
networkConnectEventSubscription = kernel::subscribeSystemEvent(
kernel::SystemEvent::NetworkConnected,
[this](kernel::SystemEvent) { onNetworkConnected(); }
);
networkConnectEventSubscription = kernel::subscribeSystemEvent(
kernel::SystemEvent::NetworkDisconnected,
[this](kernel::SystemEvent) { onNetworkDisconnected(); }
);
setEnabled(true);
}
void Development::onStop(ServiceContext& service) {
auto lock = mutex.asScopedLock();
lock.lock();
kernel::unsubscribeSystemEvent(networkConnectEventSubscription);
kernel::unsubscribeSystemEvent(networkDisconnectEventSubscription);
if (isEnabled()) {
setEnabled(false);
}
}
// region Enable/disable
void Development::setEnabled(bool enabled) {
auto lock = mutex.asScopedLock();
lock.lock();
this->enabled = enabled;
}
bool Development::isEnabled() const {
auto lock = mutex.asScopedLock();
lock.lock();
return enabled;
}
// region Enable/disable
void Development::startServer() {
auto lock = mutex.asScopedLock();
lock.lock();
if (isStarted()) {
TT_LOG_W(TAG, "Already started");
return;
}
ESP_LOGI(TAG, "Starting server");
std::stringstream stream;
stream << "{";
stream << "\"cpuFamily\":\"" << CONFIG_IDF_TARGET << "\", ";
stream << "\"osVersion\":\"" << TT_VERSION << "\", ";
stream << "\"protocolVersion\":\"1.0.0\"";
stream << "}";
deviceResponse = stream.str();
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 6666;
config.uri_match_fn = httpd_uri_match_wildcard;
if (httpd_start(&server, &config) == ESP_OK) {
httpd_register_uri_handler(server, &handleGetInfoEndpoint);
httpd_register_uri_handler(server, &appRunEndpoint);
httpd_register_uri_handler(server, &appInstallEndpoint);
TT_LOG_I(TAG, "Started on port %d", config.server_port);
} else {
TT_LOG_E(TAG, "Failed to start");
}
}
void Development::stopServer() {
auto lock = mutex.asScopedLock();
lock.lock();
if (!isStarted()) {
TT_LOG_W(TAG, "Not started");
return;
}
TT_LOG_I(TAG, "Stopping server");
if (httpd_stop(server) != ESP_OK) {
TT_LOG_W(TAG, "Error while stopping");
}
server = nullptr;
}
bool Development::isStarted() const {
auto lock = mutex.asScopedLock();
lock.lock();
return server != nullptr;
}
void Development::onNetworkConnected() {
TT_LOG_I(TAG, "onNetworkConnected");
mutex.withLock([this] {
if (isEnabled() && !isStarted()) {
startServer();
}
});
}
void Development::onNetworkDisconnected() {
TT_LOG_I(TAG, "onNetworkDisconnected");
mutex.withLock([this] {
if (isStarted()) {
stopServer();
}
});
}
// region endpoints
esp_err_t Development::handleGetInfo(httpd_req_t* request) {
if (httpd_resp_set_type(request, "application/json") != ESP_OK) {
TT_LOG_W(TAG, "Failed to send header");
return ESP_FAIL;
}
auto* service = static_cast<Development*>(request->user_ctx);
if (httpd_resp_sendstr(request, service->deviceResponse.c_str()) != ESP_OK) {
TT_LOG_W(TAG, "Failed to send response body");
return ESP_FAIL;
}
TT_LOG_I(TAG, "[200] /device");
return ESP_OK;
}
esp_err_t Development::handleAppRun(httpd_req_t* request) {
httpd_resp_send(request, nullptr, 0);
TT_LOG_I(TAG, "[200] /app/run");
return ESP_OK;
}
esp_err_t Development::handleAppInstall(httpd_req_t* request) {
httpd_resp_send(request, nullptr, 0);
TT_LOG_I(TAG, "[200] /app/install");
return ESP_OK;
}
// endregion
std::shared_ptr<Development> findService() {
return std::static_pointer_cast<Development>(
findServiceById(manifest.id)
);
}
extern const ServiceManifest manifest = {
.id = "Development",
.createService = create<Development>
};
}
#endif // ESP_PLATFORM

View File

@ -12,6 +12,7 @@
#include <atomic>
#include <cstring>
#include <Tactility/kernel/SystemEvents.h>
#include <sys/cdefs.h>
namespace tt::service::wifi {
@ -476,6 +477,7 @@ static void eventHandler(TT_UNUSED void* arg, esp_event_base_t event_base, int32
}
wifi->setRadioState(RadioState::On);
publish_event_simple(wifi, EventType::Disconnected);
kernel::publishSystemEvent(kernel::SystemEvent::NetworkDisconnected);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
auto* event = static_cast<ip_event_got_ip_t*>(event_data);
TT_LOG_I(TAG, "eventHandler: got ip:" IPSTR, IP2STR(&event->ip_info.ip));
@ -485,6 +487,7 @@ static void eventHandler(TT_UNUSED void* arg, esp_event_base_t event_base, int32
// TODO: Make thread-safe
wifi->pause_auto_connect = false; // Resume auto-connection
}
kernel::publishSystemEvent(kernel::SystemEvent::NetworkConnected);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) {
auto* event = static_cast<wifi_event_sta_scan_done_t*>(event_data);
TT_LOG_I(TAG, "eventHandler: wifi scanning done (scan id %u)", event->scan_id);