feat: integrate MCP and ES3C28P audio support with upstream compatibility updates
This commit is contained in:
@@ -37,10 +37,10 @@ void McpScreensaver::start(lv_obj_t* overlay, lv_coord_t screenW, lv_coord_t scr
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill with a dark slate background
|
||||
// Fill with a dark slate background (inverted for display path)
|
||||
size_t pixelCount = (size_t)screenW * screenH;
|
||||
for (size_t i = 0; i < pixelCount; ++i) {
|
||||
framebuffer[i] = 0x18E3; // dark blue-grey
|
||||
framebuffer[i] = ~0x18E3; // dark blue-grey
|
||||
}
|
||||
|
||||
lv_canvas_set_buffer(canvas, framebuffer, screenW, screenH, LV_COLOR_FORMAT_RGB565);
|
||||
@@ -48,12 +48,13 @@ void McpScreensaver::start(lv_obj_t* overlay, lv_coord_t screenW, lv_coord_t scr
|
||||
// Waiting label (removed on first MCP draw via lv_obj_clean)
|
||||
lv_obj_t* waitLabel = lv_label_create(canvas);
|
||||
lv_label_set_text(waitLabel, "Waiting for LLM...");
|
||||
lv_obj_set_style_text_color(waitLabel, lv_color_white(), LV_PART_MAIN);
|
||||
lv_obj_set_style_text_color(waitLabel, lv_color_black(), LV_PART_MAIN); // white on screen (inverted)
|
||||
lv_obj_align(waitLabel, LV_ALIGN_CENTER, 0, -20);
|
||||
|
||||
lv_obj_t* resLabel = lv_label_create(canvas);
|
||||
lv_label_set_text_fmt(resLabel, "Display: %dx%d", (int)screenW, (int)screenH);
|
||||
lv_obj_set_style_text_color(resLabel, lv_palette_lighten(LV_PALETTE_BLUE, 3), LV_PART_MAIN);
|
||||
lv_color_t resColor = lv_palette_lighten(LV_PALETTE_BLUE, 3);
|
||||
lv_obj_set_style_text_color(resLabel, lv_color_make(~resColor.red, ~resColor.green, ~resColor.blue), LV_PART_MAIN);
|
||||
lv_obj_align(resLabel, LV_ALIGN_CENTER, 0, 10);
|
||||
|
||||
// Register with McpSystemState
|
||||
|
||||
@@ -199,7 +199,7 @@ bool GuiService::onStart(ServiceContext& service) {
|
||||
|
||||
thread = new Thread(
|
||||
GUI_TASK_NAME,
|
||||
4096, // Last known minimum was 2800 for launching desktop
|
||||
8192, // Last known minimum was 2800 for launching desktop, increased to 8192 to prevent stack overflow on complex layouts/MCP settings
|
||||
guiMain
|
||||
);
|
||||
thread->setPriority(THREAD_PRIORITY_SERVICE);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <tactility/lvgl_icon_statusbar.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
namespace tt::service::statusbar {
|
||||
|
||||
@@ -102,6 +104,13 @@ static const char* getPowerStatusIcon() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hal::power::PowerDevice::MetricData charging_data;
|
||||
if (power->supportsMetric(hal::power::PowerDevice::MetricType::IsCharging) &&
|
||||
power->getMetric(hal::power::PowerDevice::MetricType::IsCharging, charging_data) &&
|
||||
charging_data.valueAsBool) {
|
||||
return LVGL_ICON_STATUSBAR_BATTERY_ANDROID_FRAME_BOLT;
|
||||
}
|
||||
|
||||
hal::power::PowerDevice::MetricData charge_level;
|
||||
if (!power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) {
|
||||
return nullptr;
|
||||
@@ -210,6 +219,29 @@ class StatusbarService final : public Service {
|
||||
}
|
||||
power_last_icon = desired_icon;
|
||||
}
|
||||
|
||||
std::shared_ptr<hal::power::PowerDevice> power;
|
||||
hal::findDevices<hal::power::PowerDevice>(hal::Device::Type::Power, [&power](const auto& device) {
|
||||
if (device->supportsMetric(hal::power::PowerDevice::MetricType::ChargeLevel)) {
|
||||
power = device;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (power != nullptr) {
|
||||
hal::power::PowerDevice::MetricData charge_level;
|
||||
if (power->getMetric(hal::power::PowerDevice::MetricType::ChargeLevel, charge_level)) {
|
||||
uint8_t charge = charge_level.valueAsUint8;
|
||||
std::string battery_text = std::format("{}%", charge);
|
||||
lvgl::statusbar_set_battery_text(battery_text);
|
||||
lvgl::statusbar_set_battery_visibility(true);
|
||||
} else {
|
||||
lvgl::statusbar_set_battery_visibility(false);
|
||||
}
|
||||
} else {
|
||||
lvgl::statusbar_set_battery_visibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
void updateUsbIcon() {
|
||||
|
||||
@@ -157,6 +157,116 @@ static const char* TOOLS_JSON =
|
||||
"\"mp3_base64\":{\"type\":\"string\"},"
|
||||
"\"volume\":{\"type\":\"integer\",\"default\":50}"
|
||||
"},\"required\":[\"mp3_base64\"]}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"set_led\","
|
||||
"\"description\":\"Control the onboard WS2812 NeoPixel RGB LED.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"r\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":255,\"description\":\"Red channel (0-255)\"},"
|
||||
"\"g\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":255,\"description\":\"Green channel (0-255)\"},"
|
||||
"\"b\":{\"type\":\"integer\",\"minimum\":0,\"maximum\":255,\"description\":\"Blue channel (0-255)\"},"
|
||||
"\"mode\":{\"type\":\"string\",\"enum\":[\"static\",\"breath\",\"rainbow\",\"off\"],\"description\":\"LED mode\"}"
|
||||
"},"
|
||||
"\"required\":[\"r\",\"g\",\"b\",\"mode\"]"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"get_battery\","
|
||||
"\"description\":\"Read the current battery voltage and estimated capacity percentage.\","
|
||||
"\"inputSchema\":{\"type\":\"object\",\"properties\":{}}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"get_sensors\","
|
||||
"\"description\":\"Read onboard sensors (IMU, temperature, humidity).\","
|
||||
"\"inputSchema\":{\"type\":\"object\",\"properties\":{}}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"scan_ble\","
|
||||
"\"description\":\"Scan for nearby Bluetooth Low Energy devices.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"duration_ms\":{\"type\":\"integer\",\"default\":3000,\"description\":\"Scan duration in milliseconds\"}"
|
||||
"}"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"write_file\","
|
||||
"\"description\":\"Write a file to the microSD card.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"path\":{\"type\":\"string\",\"description\":\"Destination path relative to /sdcard/\"},"
|
||||
"\"content\":{\"type\":\"string\",\"description\":\"Text content of the file\"}"
|
||||
"},"
|
||||
"\"required\":[\"path\",\"content\"]"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"read_file\","
|
||||
"\"description\":\"Read a text file from the microSD card.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"path\":{\"type\":\"string\",\"description\":\"File path relative to /sdcard/\"}"
|
||||
"},"
|
||||
"\"required\":[\"path\"]"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"download_file\","
|
||||
"\"description\":\"Download a file from a URL directly to the microSD card.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"url\":{\"type\":\"string\",\"description\":\"The URL of the file to download\"},"
|
||||
"\"filename\":{\"type\":\"string\",\"description\":\"The destination filename relative to /sdcard/\"}"
|
||||
"},"
|
||||
"\"required\":[\"url\",\"filename\"]"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"get_video_streaming_instructions\","
|
||||
"\"description\":\"Get details for monochrome TCP streaming and RGB565 color TCP streaming.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"protocol\":{\"type\":\"string\",\"enum\":[\"tcp\",\"color\",\"both\",\"all\"],\"default\":\"all\"}"
|
||||
"}"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"get_stream_stats\","
|
||||
"\"description\":\"Get performance statistics for the active video streams.\","
|
||||
"\"inputSchema\":{\"type\":\"object\",\"properties\":{}}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"list_apps\","
|
||||
"\"description\":\"List all applications installed on the system, including metadata and locations.\","
|
||||
"\"inputSchema\":{\"type\":\"object\",\"properties\":{}}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"run_app\","
|
||||
"\"description\":\"Launch an application on the board by its App ID.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"app_id\":{\"type\":\"string\",\"description\":\"The unique ID of the application (e.g., 'one.tactility.helloworld')\"}"
|
||||
"},"
|
||||
"\"required\":[\"app_id\"]"
|
||||
"}"
|
||||
"},"
|
||||
"{"
|
||||
"\"name\":\"list_files\","
|
||||
"\"description\":\"List files and folders in a directory on the SD card, with details like sizes.\","
|
||||
"\"inputSchema\":{"
|
||||
"\"type\":\"object\","
|
||||
"\"properties\":{"
|
||||
"\"path\":{\"type\":\"string\",\"description\":\"Path relative to /sdcard/ to list (default is '/')\"}"
|
||||
"}"
|
||||
"}"
|
||||
"}"
|
||||
"]";
|
||||
|
||||
@@ -173,7 +283,7 @@ static uint8_t* base64_decode(const char* input, size_t* output_size) {
|
||||
|
||||
size_t input_size = strlen(payload);
|
||||
size_t approx_output_len = (input_size * 3) / 4;
|
||||
uint8_t* output = (uint8_t*)psram_malloc(approx_output_len + 4);
|
||||
uint8_t* output = (uint8_t*)malloc(approx_output_len + 4);
|
||||
if (output == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -564,6 +674,162 @@ static cJSON* handle_tool_call(cJSON* id, cJSON* params) {
|
||||
? make_tool_result(id, "Successfully played base64 MP3 audio.")
|
||||
: make_error(id, -32020, error.c_str());
|
||||
}
|
||||
|
||||
if (strcmp(name, "set_led") == 0) {
|
||||
cJSON* r_item = cJSON_GetObjectItemCaseSensitive(arguments, "r");
|
||||
cJSON* g_item = cJSON_GetObjectItemCaseSensitive(arguments, "g");
|
||||
cJSON* b_item = cJSON_GetObjectItemCaseSensitive(arguments, "b");
|
||||
cJSON* mode_item = cJSON_GetObjectItemCaseSensitive(arguments, "mode");
|
||||
if (!cJSON_IsNumber(r_item) || !cJSON_IsNumber(g_item) ||
|
||||
!cJSON_IsNumber(b_item) || !cJSON_IsString(mode_item)) {
|
||||
return make_error(id, -32602, "set_led requires r, g, b, and mode");
|
||||
}
|
||||
std::string error;
|
||||
if (!mcp::setLedColor(r_item->valueint, g_item->valueint, b_item->valueint, mode_item->valuestring, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "LED set to mode '%s' with base color (%d, %d, %d).",
|
||||
mode_item->valuestring, r_item->valueint, g_item->valueint, b_item->valueint);
|
||||
return make_tool_result(id, msg);
|
||||
}
|
||||
|
||||
if (strcmp(name, "get_battery") == 0) {
|
||||
double voltage_v = 0.0;
|
||||
int percentage_pct = 0;
|
||||
std::string error;
|
||||
if (!mcp::getBatteryStatus(voltage_v, percentage_pct, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
char result_buf[128];
|
||||
snprintf(result_buf, sizeof(result_buf), "{\"voltage_v\":%.3f,\"percentage_pct\":%d}", voltage_v, percentage_pct);
|
||||
return make_tool_result(id, result_buf);
|
||||
}
|
||||
|
||||
if (strcmp(name, "get_sensors") == 0) {
|
||||
double temp_c = 0.0;
|
||||
double hum_pct = 0.0;
|
||||
std::string imu_json;
|
||||
std::string error;
|
||||
if (!mcp::getSensors(temp_c, hum_pct, imu_json, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
char result_buf[512];
|
||||
snprintf(result_buf, sizeof(result_buf), "{\"temperature_c\":%.2f,\"humidity_pct\":%.2f,\"imu\":%s}",
|
||||
temp_c, hum_pct, imu_json.c_str());
|
||||
return make_tool_result(id, result_buf);
|
||||
}
|
||||
|
||||
if (strcmp(name, "scan_ble") == 0) {
|
||||
cJSON* duration_item = cJSON_GetObjectItemCaseSensitive(arguments, "duration_ms");
|
||||
int duration_ms = cJSON_IsNumber(duration_item) ? duration_item->valueint : 3000;
|
||||
if (duration_ms < 500 || duration_ms > 15000) {
|
||||
return make_error(id, -32602, "duration_ms must be between 500 and 15000");
|
||||
}
|
||||
std::string devices_json;
|
||||
std::string error;
|
||||
if (!mcp::scanBleDevices(duration_ms, devices_json, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
return make_tool_result(id, devices_json.c_str());
|
||||
}
|
||||
|
||||
if (strcmp(name, "write_file") == 0) {
|
||||
cJSON* path_item = cJSON_GetObjectItemCaseSensitive(arguments, "path");
|
||||
cJSON* content_item = cJSON_GetObjectItemCaseSensitive(arguments, "content");
|
||||
if (!cJSON_IsString(path_item) || !cJSON_IsString(content_item)) {
|
||||
return make_error(id, -32602, "write_file requires path and content");
|
||||
}
|
||||
std::string error;
|
||||
if (!mcp::writeSdFile(path_item->valuestring, content_item->valuestring, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
return make_tool_result(id, "Successfully wrote file to SD card.");
|
||||
}
|
||||
|
||||
if (strcmp(name, "read_file") == 0) {
|
||||
cJSON* path_item = cJSON_GetObjectItemCaseSensitive(arguments, "path");
|
||||
if (!cJSON_IsString(path_item)) {
|
||||
return make_error(id, -32602, "read_file requires path");
|
||||
}
|
||||
std::string content;
|
||||
std::string error;
|
||||
if (!mcp::readSdFile(path_item->valuestring, content, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
return make_tool_result(id, content.c_str());
|
||||
}
|
||||
|
||||
if (strcmp(name, "download_file") == 0) {
|
||||
cJSON* url_item = cJSON_GetObjectItemCaseSensitive(arguments, "url");
|
||||
cJSON* filename_item = cJSON_GetObjectItemCaseSensitive(arguments, "filename");
|
||||
if (!cJSON_IsString(url_item) || !cJSON_IsString(filename_item)) {
|
||||
return make_error(id, -32602, "download_file requires url and filename");
|
||||
}
|
||||
std::string error;
|
||||
if (!mcp::downloadSdFile(url_item->valuestring, filename_item->valuestring, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
return make_tool_result(id, "Successfully downloaded file to SD card.");
|
||||
}
|
||||
|
||||
if (strcmp(name, "get_video_streaming_instructions") == 0) {
|
||||
cJSON* protocol_item = cJSON_GetObjectItemCaseSensitive(arguments, "protocol");
|
||||
const char* protocol = cJSON_IsString(protocol_item) ? protocol_item->valuestring : "all";
|
||||
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Tactility OS TCP Video Streaming Instructions:\n"
|
||||
"1. Mono TCP Stream (compatibility mode): Stream raw 15,000-byte PBM/RLCD frames to TCP port 8081.\n"
|
||||
"2. Color TCP Stream (native): Stream packets to TCP port 8083.\n"
|
||||
" Packet format:\n"
|
||||
" - Header (16 bytes): Magic 'RAW\\x01' (4 bytes), X-coord (2 bytes BE), Y-coord (2 bytes BE), Width (2 bytes BE), Height (2 bytes BE), Payload length (4 bytes BE).\n"
|
||||
" - Payload (variable): Raw big-endian RGB565 pixel data of length 'Payload length'.\n"
|
||||
"Active protocol selection: %s", protocol);
|
||||
return make_tool_result(id, buf);
|
||||
}
|
||||
|
||||
if (strcmp(name, "get_stream_stats") == 0) {
|
||||
std::string stats_json;
|
||||
if (!mcp::getVideoStreamStats(stats_json)) {
|
||||
return make_error(id, -32020, "Failed to get stream stats");
|
||||
}
|
||||
return make_tool_result(id, stats_json.c_str());
|
||||
}
|
||||
|
||||
if (strcmp(name, "list_apps") == 0) {
|
||||
std::string apps_json;
|
||||
std::string error;
|
||||
if (!mcp::listApps(apps_json, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
return make_tool_result(id, apps_json.c_str());
|
||||
}
|
||||
|
||||
if (strcmp(name, "run_app") == 0) {
|
||||
cJSON* app_id_item = cJSON_GetObjectItemCaseSensitive(arguments, "app_id");
|
||||
if (!cJSON_IsString(app_id_item)) {
|
||||
return make_error(id, -32602, "run_app requires app_id");
|
||||
}
|
||||
std::string error;
|
||||
if (!mcp::runApp(app_id_item->valuestring, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "Successfully started application '%s'.", app_id_item->valuestring);
|
||||
return make_tool_result(id, msg);
|
||||
}
|
||||
|
||||
if (strcmp(name, "list_files") == 0) {
|
||||
cJSON* path_item = cJSON_GetObjectItemCaseSensitive(arguments, "path");
|
||||
const char* path = cJSON_IsString(path_item) ? path_item->valuestring : "/";
|
||||
std::string files_json;
|
||||
std::string error;
|
||||
if (!mcp::listSdFiles(path, files_json, error)) {
|
||||
return make_error(id, -32020, error.c_str());
|
||||
}
|
||||
return make_tool_result(id, files_json.c_str());
|
||||
}
|
||||
|
||||
return make_error(id, -32602, "Unknown tool");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <Tactility/settings/WebServerSettings.h>
|
||||
#include <Tactility/settings/McpSettings.h>
|
||||
#include <Tactility/mcp/McpSystem.h>
|
||||
#include <Tactility/MountPoints.h>
|
||||
#include <Tactility/file/File.h>
|
||||
#include <Tactility/Logger.h>
|
||||
@@ -510,7 +511,7 @@ bool WebServerService::startServer() {
|
||||
settings.webServerPort,
|
||||
"0.0.0.0",
|
||||
handlers,
|
||||
16384 // Stack size
|
||||
12288 // Stack size (forces allocation in internal SRAM instead of PSRAM)
|
||||
);
|
||||
|
||||
httpServer->start();
|
||||
@@ -531,6 +532,11 @@ bool WebServerService::startServer() {
|
||||
settings.wifiMode == settings::webserver::WiFiMode::AccessPoint ? "AP" : "Station");
|
||||
}
|
||||
|
||||
auto mcpSettings = settings::mcp::loadOrGetDefault();
|
||||
if (mcpSettings.mcpEnabled) {
|
||||
mcp::startVideoStreamServer();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -542,6 +548,8 @@ void WebServerService::stopServer() {
|
||||
httpServer->stop();
|
||||
httpServer.reset();
|
||||
|
||||
mcp::stopVideoStreamServer();
|
||||
|
||||
// Stop AP mode WiFi if we started it
|
||||
if (apWifiInitialized || apNetif != nullptr) {
|
||||
stopApMode();
|
||||
|
||||
@@ -107,6 +107,23 @@ static bool decrypt(const std::string& ssidInput, std::string& ssidOutput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Strip PKCS#7 padding
|
||||
if (result_length > 0) {
|
||||
uint8_t pad_val = result[result_length - 1];
|
||||
if (pad_val >= 1 && pad_val <= 16 && pad_val <= result_length) {
|
||||
bool valid_padding = true;
|
||||
for (size_t i = result_length - pad_val; i < result_length; ++i) {
|
||||
if (result[i] != pad_val) {
|
||||
valid_padding = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid_padding) {
|
||||
result[result_length - pad_val] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssidOutput = reinterpret_cast<char*>(result);
|
||||
free(result);
|
||||
return true;
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <Tactility/service/wifi/WifiSettings.h>
|
||||
|
||||
#include <esp_wifi_default.h>
|
||||
#include <esp_mac.h>
|
||||
#include <esp_netif.h>
|
||||
#include <mdns.h>
|
||||
#include <lwip/esp_netif_net_stack.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <atomic>
|
||||
@@ -543,6 +546,28 @@ static void dispatchEnable(std::shared_ptr<Wifi> wifi) {
|
||||
}
|
||||
wifi->netif = esp_netif_create_default_wifi_sta();
|
||||
|
||||
if (wifi->netif != nullptr) {
|
||||
uint8_t mac[6];
|
||||
char hostname[32];
|
||||
if (esp_read_mac(mac, ESP_MAC_WIFI_STA) == ESP_OK) {
|
||||
snprintf(hostname, sizeof(hostname), "kidsOS-%02X%02X", mac[4], mac[5]);
|
||||
} else {
|
||||
strncpy(hostname, "kidsOS", sizeof(hostname));
|
||||
}
|
||||
LOGGER.info("Setting DHCP Hostname to '{}'", hostname);
|
||||
esp_netif_set_hostname(wifi->netif, hostname);
|
||||
|
||||
// Initialize mDNS
|
||||
esp_err_t mdns_err = mdns_init();
|
||||
if (mdns_err == ESP_OK) {
|
||||
LOGGER.info("mDNS initialized, setting hostname to '{}.local'", hostname);
|
||||
mdns_hostname_set(hostname);
|
||||
mdns_instance_name_set("kidsOS Tactility Device");
|
||||
} else {
|
||||
LOGGER.error("Failed to initialize mDNS: {}", (int)mdns_err);
|
||||
}
|
||||
}
|
||||
|
||||
// Warning: this is the memory-intensive operation
|
||||
// It uses over 117kB of RAM with default settings for S3 on IDF v5.1.2
|
||||
wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
|
||||
|
||||
Reference in New Issue
Block a user