diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index c534cba..9ff3db3 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -40,6 +40,7 @@ idf_component_register( "platform/internal" REQUIRES bt + esp_coex esp_wifi esp_netif esp_event diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 12ff8e6..c6866ab 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -34,7 +34,7 @@ config TQ_Z_IMAGE_MODEL config TQ_Z_IMAGE_DEFAULT_SIZE string "Z-Image default output size" - default "1024*1536" + default "512*768" config TQ_Z_IMAGE_TIMEOUT_MS int "Z-Image generation timeout (ms)" @@ -56,6 +56,20 @@ config TQ_Z_IMAGE_MAX_PNG_BYTES range 65536 8388608 default 4194304 +config TQ_Z_IMAGE_HTTP_BUFFER_BYTES + int "Z-Image HTTP client buffer bytes" + range 4096 32768 + default 16384 + +config TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES + int "Z-Image HTTP stream read chunk bytes" + range 4096 32768 + default 16384 + +config TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD + bool "Prefer Wi-Fi RF during image download" + default y + config TQ_VOICE_API_KEY string "Voice API Key for DashScope WebSocket (optional)" default "sk-7a50eca6856d4afb968ac3bf512f6d1b" diff --git a/main/domain/src/image_generation.c b/main/domain/src/image_generation.c index a421a9d..4db6473 100644 --- a/main/domain/src/image_generation.c +++ b/main/domain/src/image_generation.c @@ -5,10 +5,15 @@ #include #include "cJSON.h" +#include "esp_coexist.h" #include "esp_crt_bundle.h" #include "esp_heap_caps.h" #include "esp_http_client.h" #include "esp_log.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" #ifndef CONFIG_TQ_Z_IMAGE_API_ENDPOINT #define CONFIG_TQ_Z_IMAGE_API_ENDPOINT "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation" @@ -19,7 +24,7 @@ #endif #ifndef CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE -#define CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE "1024*1536" +#define CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE "512*768" #endif #ifndef CONFIG_TQ_Z_IMAGE_TIMEOUT_MS @@ -38,8 +43,31 @@ #define CONFIG_TQ_Z_IMAGE_MAX_PNG_BYTES (4 * 1024 * 1024) #endif +#ifndef CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES +#define CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES 16384 +#endif + +#ifndef CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES +#define CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES 16384 +#endif + +#define IMAGE_HTTP_READ_CHUNK_BYTES ((size_t)CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES) +#define IMAGE_HTTP_BUFFER_BYTES CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES +#define IMAGE_PREWARM_TIMEOUT_MS 2500 +#define IMAGE_PREWARM_STACK 5120 + +#if CONFIG_FREERTOS_UNICORE +#define IMAGE_WORKER_CORE_ID 0 +#else +#define IMAGE_WORKER_CORE_ID 1 +#endif + static const char *TAG = "image_generation"; +typedef struct { + char oss_origin[160]; +} image_prewarm_task_arg_t; + typedef struct { uint8_t *data; size_t len; @@ -47,6 +75,40 @@ typedef struct { size_t max_len; } bytes_buffer_t; +static SemaphoreHandle_t s_prewarm_lock; +static bool s_prewarm_running; +static char s_last_oss_origin[160] = "https://dashscope-result-bj.oss-cn-beijing.aliyuncs.com/"; + +static bool image_generation_boost_coex_for_download(void) { +#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD + esp_err_t rc = esp_coex_preference_set(ESP_COEX_PREFER_WIFI); + if (rc != ESP_OK) { + ESP_LOGW(TAG, "failed to prefer Wi-Fi coexist mode, rc=0x%x", (unsigned)rc); + return false; + } + ESP_LOGI(TAG, "coexist prefer Wi-Fi enabled for image download"); + return true; +#else + return false; +#endif +} + +static void image_generation_restore_coex_after_download(bool boosted) { +#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD + if (!boosted) { + return; + } + esp_err_t rc = esp_coex_preference_set(ESP_COEX_PREFER_BALANCE); + if (rc != ESP_OK) { + ESP_LOGW(TAG, "failed to restore coexist balance mode, rc=0x%x", (unsigned)rc); + return; + } + ESP_LOGI(TAG, "coexist preference restored to balance"); +#else + (void)boosted; +#endif +} + static void image_generation_fill_err(char *err, size_t err_len, const char *msg) { if (err != NULL && err_len > 0) { snprintf(err, err_len, "%s", msg != NULL ? msg : "unknown error"); @@ -149,6 +211,206 @@ static const char *image_generation_api_key(void) { return NULL; } +static void prewarm_lock_init_if_needed(void) { + if (s_prewarm_lock != NULL) { + return; + } + s_prewarm_lock = xSemaphoreCreateMutex(); +} + +static bool url_extract_origin(const char *url, char *out_origin, size_t out_origin_len) { + if (url == NULL || out_origin == NULL || out_origin_len == 0) { + return false; + } + + const char *scheme = strstr(url, "://"); + if (scheme == NULL || scheme == url) { + return false; + } + + const char *host = scheme + 3; + if (*host == '\0') { + return false; + } + + const char *path = strchr(host, '/'); + size_t host_len = path != NULL ? (size_t)(path - host) : strlen(host); + if (host_len == 0) { + return false; + } + + int n = snprintf(out_origin, + out_origin_len, + "%.*s://%.*s/", + (int)(scheme - url), + url, + (int)host_len, + host); + return n > 0 && (size_t)n < out_origin_len; +} + +static void image_generation_remember_oss_origin(const char *image_url) { + char origin[160] = {0}; + if (!url_extract_origin(image_url, origin, sizeof(origin))) { + return; + } + + prewarm_lock_init_if_needed(); + if (s_prewarm_lock == NULL) { + return; + } + + if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) != pdTRUE) { + return; + } + strlcpy(s_last_oss_origin, origin, sizeof(s_last_oss_origin)); + xSemaphoreGive(s_prewarm_lock); +} + +static void image_generation_copy_oss_origin(char *out_origin, size_t out_origin_len) { + if (out_origin == NULL || out_origin_len == 0) { + return; + } + out_origin[0] = '\0'; + + prewarm_lock_init_if_needed(); + if (s_prewarm_lock == NULL) { + strlcpy(out_origin, s_last_oss_origin, out_origin_len); + return; + } + + if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) != pdTRUE) { + strlcpy(out_origin, s_last_oss_origin, out_origin_len); + return; + } + strlcpy(out_origin, s_last_oss_origin, out_origin_len); + xSemaphoreGive(s_prewarm_lock); +} + +static esp_err_t image_generation_http_prewarm_head(const char *url, + const char *auth_header, + uint32_t timeout_ms) { + if (url == NULL || url[0] == '\0') { + return ESP_ERR_INVALID_ARG; + } + + esp_http_client_config_t cfg = { + .url = url, + .method = HTTP_METHOD_HEAD, + .timeout_ms = (int)timeout_ms, + .keep_alive_enable = true, + .buffer_size = 1024, + .buffer_size_tx = 1024, + .crt_bundle_attach = esp_crt_bundle_attach, + }; + + esp_http_client_handle_t client = esp_http_client_init(&cfg); + if (client == NULL) { + return ESP_FAIL; + } + + if (auth_header != NULL && auth_header[0] != '\0') { + esp_http_client_set_header(client, "Authorization", auth_header); + } + + esp_err_t rc = esp_http_client_open(client, 0); + if (rc == ESP_OK) { + (void)esp_http_client_fetch_headers(client); + esp_http_client_close(client); + } + esp_http_client_cleanup(client); + return rc; +} + +static void image_generation_prewarm_task(void *arg) { + image_prewarm_task_arg_t *task_arg = (image_prewarm_task_arg_t *)arg; + char oss_origin[160] = {0}; + if (task_arg != NULL) { + strlcpy(oss_origin, task_arg->oss_origin, sizeof(oss_origin)); + free(task_arg); + } + + int64_t start_ms = esp_timer_get_time() / 1000; + const char *api_key = image_generation_api_key(); + char auth_header[192] = {0}; + const char *auth = NULL; + if (api_key != NULL) { + snprintf(auth_header, sizeof(auth_header), "Bearer %s", api_key); + auth = auth_header; + } + + esp_err_t gen_rc = image_generation_http_prewarm_head(CONFIG_TQ_Z_IMAGE_API_ENDPOINT, + auth, + IMAGE_PREWARM_TIMEOUT_MS); + esp_err_t oss_rc = image_generation_http_prewarm_head(oss_origin, + NULL, + IMAGE_PREWARM_TIMEOUT_MS); + int64_t done_ms = esp_timer_get_time() / 1000; + + ESP_LOGI(TAG, + "https prewarm done, gen_rc=0x%x, oss_rc=0x%x, cost_ms=%lld", + (unsigned)gen_rc, + (unsigned)oss_rc, + (long long)(done_ms - start_ms)); + + prewarm_lock_init_if_needed(); + if (s_prewarm_lock != NULL && xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) == pdTRUE) { + s_prewarm_running = false; + xSemaphoreGive(s_prewarm_lock); + } else { + s_prewarm_running = false; + } + + vTaskDelete(NULL); +} + +void image_generation_schedule_prewarm(void) { + prewarm_lock_init_if_needed(); + if (s_prewarm_lock == NULL) { + return; + } + + if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) != pdTRUE) { + return; + } + if (s_prewarm_running) { + xSemaphoreGive(s_prewarm_lock); + return; + } + s_prewarm_running = true; + xSemaphoreGive(s_prewarm_lock); + + image_prewarm_task_arg_t *arg = (image_prewarm_task_arg_t *)calloc(1, sizeof(*arg)); + if (arg == NULL) { + if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) == pdTRUE) { + s_prewarm_running = false; + xSemaphoreGive(s_prewarm_lock); + } else { + s_prewarm_running = false; + } + return; + } + image_generation_copy_oss_origin(arg->oss_origin, sizeof(arg->oss_origin)); + + BaseType_t ok = xTaskCreatePinnedToCore(image_generation_prewarm_task, + "img_prewarm", + IMAGE_PREWARM_STACK, + arg, + 4, + NULL, + IMAGE_WORKER_CORE_ID); + if (ok != pdPASS) { + free(arg); + if (xSemaphoreTake(s_prewarm_lock, pdMS_TO_TICKS(100)) == pdTRUE) { + s_prewarm_running = false; + xSemaphoreGive(s_prewarm_lock); + } else { + s_prewarm_running = false; + } + ESP_LOGW(TAG, "failed to create prewarm task"); + } +} + static esp_err_t http_read_full_response(esp_http_client_handle_t client, bytes_buffer_t *out_body, char *err, @@ -158,14 +420,21 @@ static esp_err_t http_read_full_response(esp_http_client_handle_t client, return ESP_ERR_INVALID_ARG; } - char tmp[1024]; + char *tmp = (char *)malloc_prefer_psram(IMAGE_HTTP_READ_CHUNK_BYTES); + if (tmp == NULL) { + image_generation_fill_err(err, err_len, "no memory for http read buffer"); + return ESP_ERR_NO_MEM; + } + while (true) { - int n = esp_http_client_read(client, tmp, sizeof(tmp)); + int n = esp_http_client_read(client, tmp, IMAGE_HTTP_READ_CHUNK_BYTES); if (n < 0) { + free(tmp); image_generation_fill_err(err, err_len, "http read failed"); return ESP_FAIL; } if (n == 0) { + free(tmp); if (esp_http_client_is_complete_data_received(client)) { return ESP_OK; } @@ -175,6 +444,7 @@ static esp_err_t http_read_full_response(esp_http_client_handle_t client, esp_err_t append_rc = bytes_buffer_append(out_body, tmp, (size_t)n); if (append_rc != ESP_OK) { + free(tmp); image_generation_fill_err(err, err_len, "http response too large"); return append_rc; } @@ -231,6 +501,16 @@ static esp_err_t http_request_collect(const esp_http_client_config_t *config, image_generation_fill_err(err, err_len, "http response too large"); return ESP_ERR_INVALID_SIZE; } + + if (content_len > 0) { + esp_err_t reserve_rc = bytes_buffer_reserve(out_body, (size_t)content_len); + if (reserve_rc != ESP_OK) { + esp_http_client_close(client); + esp_http_client_cleanup(client); + image_generation_fill_err(err, err_len, "no memory for http response"); + return reserve_rc; + } + } if (out_status_code != NULL) { *out_status_code = esp_http_client_get_status_code(client); } @@ -402,9 +682,9 @@ static esp_err_t image_generation_invoke_model(const image_generation_request_t .url = CONFIG_TQ_Z_IMAGE_API_ENDPOINT, .method = HTTP_METHOD_POST, .timeout_ms = (int)req->generation_timeout_ms, - .keep_alive_enable = false, - .buffer_size = 2048, - .buffer_size_tx = 2048, + .keep_alive_enable = true, + .buffer_size = IMAGE_HTTP_BUFFER_BYTES, + .buffer_size_tx = IMAGE_HTTP_BUFFER_BYTES, .crt_bundle_attach = esp_crt_bundle_attach, }; @@ -491,6 +771,8 @@ static esp_err_t image_generation_invoke_model(const image_generation_request_t return ESP_FAIL; } + image_generation_remember_oss_origin(out_result->image_url); + ESP_LOGI(TAG, "z-image response ready, request_id=%s, model_size=%ux%u, image_url_len=%u", out_result->request_id[0] != '\0' ? out_result->request_id : "-", @@ -507,13 +789,14 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re char *err, size_t err_len) { ESP_LOGI(TAG, "generated image download start, timeout_ms=%u", (unsigned)timeout_ms); + bool coex_boosted = image_generation_boost_coex_for_download(); esp_http_client_config_t config = { .url = out_result->image_url, .method = HTTP_METHOD_GET, .timeout_ms = (int)timeout_ms, - .keep_alive_enable = false, - .buffer_size = 2048, + .keep_alive_enable = true, + .buffer_size = IMAGE_HTTP_BUFFER_BYTES, .crt_bundle_attach = esp_crt_bundle_attach, }; @@ -537,6 +820,7 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re (unsigned)rc, (err != NULL && err[0] != '\0') ? err : "download failed"); bytes_buffer_free(&resp); + image_generation_restore_coex_after_download(coex_boosted); return rc; } @@ -545,6 +829,7 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re if (rc != ESP_OK) { bytes_buffer_free(&resp); image_generation_fill_err(err, err_len, "image download failed"); + image_generation_restore_coex_after_download(coex_boosted); return ESP_FAIL; } extract_remote_error_message((const char *)resp.data, http_status, err, err_len); @@ -553,6 +838,7 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re http_status, (err != NULL && err[0] != '\0') ? err : "image download failed"); bytes_buffer_free(&resp); + image_generation_restore_coex_after_download(coex_boosted); return ESP_FAIL; } @@ -563,12 +849,14 @@ static esp_err_t image_generation_download_png(image_generation_result_t *out_re bytes_buffer_free(&resp); image_generation_fill_err(err, err_len, "downloaded image is not png"); ESP_LOGW(TAG, "generated image download invalid png signature"); + image_generation_restore_coex_after_download(coex_boosted); return ESP_FAIL; } out_result->png = resp.data; out_result->png_len = resp.len; ESP_LOGI(TAG, "generated image download done, png_bytes=%u", (unsigned)out_result->png_len); + image_generation_restore_coex_after_download(coex_boosted); return ESP_OK; } @@ -615,6 +903,8 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req, actual.has_seed ? "set" : "auto", (unsigned)actual.generation_timeout_ms, (unsigned)actual.download_timeout_ms); + + int64_t t0_ms = esp_timer_get_time() / 1000; esp_err_t gen_rc = image_generation_invoke_model(&actual, out_result, err, err_len); if (gen_rc != ESP_OK) { ESP_LOGW(TAG, @@ -625,7 +915,11 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req, return gen_rc; } - ESP_LOGI(TAG, "image generate stage done, request_id=%s", out_result->request_id[0] != '\0' ? out_result->request_id : "-"); + int64_t t_gen_done_ms = esp_timer_get_time() / 1000; + ESP_LOGI(TAG, + "image generate stage done, request_id=%s, gen_cost_ms=%lld", + out_result->request_id[0] != '\0' ? out_result->request_id : "-", + (long long)(t_gen_done_ms - t0_ms)); esp_err_t dl_rc = image_generation_download_png(out_result, actual.download_timeout_ms, err, err_len); if (dl_rc != ESP_OK) { ESP_LOGW(TAG, @@ -636,10 +930,15 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req, return dl_rc; } + int64_t t_dl_done_ms = esp_timer_get_time() / 1000; + ESP_LOGI(TAG, - "image generation pipeline done, request_id=%s, png_bytes=%u", + "image generation pipeline done, request_id=%s, png_bytes=%u, gen_ms=%lld, download_ms=%lld, total_ms=%lld", out_result->request_id[0] != '\0' ? out_result->request_id : "-", - (unsigned)out_result->png_len); + (unsigned)out_result->png_len, + (long long)(t_gen_done_ms - t0_ms), + (long long)(t_dl_done_ms - t_gen_done_ms), + (long long)(t_dl_done_ms - t0_ms)); return ESP_OK; } diff --git a/main/domain/src/raster_tools_image_qr.c b/main/domain/src/raster_tools_image_qr.c index 0f40259..6a29ad5 100644 --- a/main/domain/src/raster_tools_image_qr.c +++ b/main/domain/src/raster_tools_image_qr.c @@ -14,6 +14,8 @@ #define PNG_DECODE_MAX_BYTES (2 * 1024 * 1024) #define PNG_DECODE_MAX_WIDTH 1600 +static const uint8_t s_bit_mask[8] = {0x80u, 0x40u, 0x20u, 0x10u, 0x08u, 0x04u, 0x02u, 0x01u}; + static void set_black(uint8_t *buf, uint16_t width, uint16_t x, uint16_t y) { if (x >= width) { return; @@ -40,30 +42,6 @@ static void *calloc_prefer_psram(size_t n, size_t size) { return ptr; } -static uint8_t sample_gray8_scaled(const uint8_t *gray, - uint16_t src_width, - uint16_t src_height, - bool scale_to_width, - uint16_t dst_height, - uint16_t x, - uint16_t y) { - uint16_t src_y = scale_to_width - ? (uint16_t)(((uint32_t)y * src_height) / dst_height) - : y; - if (src_y >= src_height) { - src_y = (uint16_t)(src_height - 1); - } - - uint16_t src_x = scale_to_width - ? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH) - : x; - if (src_x >= src_width) { - src_x = (uint16_t)(src_width - 1); - } - - return gray[(size_t)src_y * src_width + src_x]; -} - static uint8_t gray8_to_work_luma(uint8_t gray, bool invert) { return invert ? (uint8_t)(255u - gray) : gray; } @@ -165,9 +143,45 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray, return ESP_ERR_INVALID_SIZE; } + uint16_t *x_map = (uint16_t *)alloc_prefer_psram(sizeof(uint16_t) * RASTER_WIDTH); + uint16_t *y_map = (uint16_t *)alloc_prefer_psram(sizeof(uint16_t) * dst_height); + size_t total_px = (size_t)RASTER_WIDTH * dst_height; + uint8_t *luma = (uint8_t *)alloc_prefer_psram(total_px); + if (x_map == NULL || y_map == NULL || luma == NULL) { + free(x_map); + free(y_map); + free(luma); + if (err != NULL && err_len > 0) { + snprintf(err, err_len, "no memory"); + } + return ESP_ERR_NO_MEM; + } + + for (uint16_t x = 0; x < RASTER_WIDTH; ++x) { + uint16_t src_x = scale_to_width + ? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH) + : x; + if (src_x >= src_width) { + src_x = (uint16_t)(src_width - 1); + } + x_map[x] = src_x; + } + for (uint16_t y = 0; y < dst_height; ++y) { + uint16_t src_y = scale_to_width + ? (uint16_t)(((uint32_t)y * src_height) / dst_height) + : y; + if (src_y >= src_height) { + src_y = (uint16_t)(src_height - 1); + } + y_map[y] = src_y; + } + size_t len = (size_t)RASTER_BYTES_PER_ROW * dst_height; uint8_t *raster = (uint8_t *)calloc_prefer_psram(1, len); if (raster == NULL) { + free(x_map); + free(y_map); + free(luma); if (err != NULL && err_len > 0) { snprintf(err, err_len, "no memory"); } @@ -176,17 +190,19 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray, uint32_t hist[256] = {0}; uint64_t sum_luma = 0; - uint32_t total_px = (uint32_t)RASTER_WIDTH * (uint32_t)dst_height; for (uint16_t y = 0; y < dst_height; ++y) { + const uint8_t *src_row = gray + (size_t)y_map[y] * src_width; + uint8_t *dst_luma_row = luma + (size_t)y * RASTER_WIDTH; for (uint16_t x = 0; x < RASTER_WIDTH; ++x) { - uint8_t src_v = sample_gray8_scaled(gray, src_width, src_height, scale_to_width, dst_height, x, y); + uint8_t src_v = src_row[x_map[x]]; uint8_t work_v = gray8_to_work_luma(src_v, invert); + dst_luma_row[x] = work_v; hist[work_v]++; sum_luma += work_v; } } - uint8_t otsu_thr = compute_otsu_threshold(hist, total_px); + uint8_t otsu_thr = compute_otsu_threshold(hist, (uint32_t)total_px); uint8_t mean_luma = total_px > 0 ? (uint8_t)(sum_luma / total_px) : (uint8_t)160; /* Keep backward compatibility: threshold still works as bias around 160. */ @@ -208,26 +224,27 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray, bool use_dither = (err_curr != NULL && err_next != NULL); for (uint16_t y = 0; y < dst_height; ++y) { + const uint8_t *luma_row = luma + (size_t)y * RASTER_WIDTH; + uint8_t *raster_row = raster + (size_t)y * RASTER_BYTES_PER_ROW; for (uint16_t x = 0; x < RASTER_WIDTH; ++x) { - uint8_t src_v = sample_gray8_scaled(gray, src_width, src_height, scale_to_width, dst_height, x, y); - int luma = (int)gray8_to_work_luma(src_v, invert); + int px_luma = (int)luma_row[x]; if (use_dither) { - luma += err_curr[x + 1u]; - if (luma < 0) { - luma = 0; - } else if (luma > 255) { - luma = 255; + px_luma += err_curr[x + 1u]; + if (px_luma < 0) { + px_luma = 0; + } else if (px_luma > 255) { + px_luma = 255; } } - bool black = luma < final_thr; + bool black = px_luma < final_thr; if (black) { - set_black(raster, RASTER_WIDTH, x, y); + raster_row[x >> 3] |= s_bit_mask[x & 7u]; } if (use_dither) { int quantized = black ? 0 : 255; - int e = luma - quantized; + int e = px_luma - quantized; if ((x + 1u) < RASTER_WIDTH) { err_curr[x + 2u] += (int16_t)((e * 7) / 16); @@ -248,6 +265,9 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray, free(err_curr); free(err_next); + free(x_map); + free(y_map); + free(luma); *out_width = dst_width; *out_height = dst_height; diff --git a/main/platform/src/wifi_manager.c b/main/platform/src/wifi_manager.c index 590edd7..ee5403d 100644 --- a/main/platform/src/wifi_manager.c +++ b/main/platform/src/wifi_manager.c @@ -20,6 +20,30 @@ static int s_retry_num; static bool s_ready; static esp_netif_t *s_sta_netif; +static void wifi_manager_apply_sta_throughput_profile(void) { + const uint8_t protocol = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N; + esp_err_t proto_rc = esp_wifi_set_protocol(WIFI_IF_STA, protocol); + if (proto_rc != ESP_OK) { + ESP_LOGW(TAG, "failed to set 11bgn protocol mask, rc=0x%x", (unsigned)proto_rc); + } else { + ESP_LOGI(TAG, "Wi-Fi protocol mask set to 11b/11g/11n"); + } + + esp_err_t bw_rc = esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT40); + if (bw_rc == ESP_OK) { + ESP_LOGI(TAG, "Wi-Fi bandwidth set to HT40"); + return; + } + + ESP_LOGW(TAG, "failed to set HT40, fallback to HT20, rc=0x%x", (unsigned)bw_rc); + bw_rc = esp_wifi_set_bandwidth(WIFI_IF_STA, WIFI_BW_HT20); + if (bw_rc != ESP_OK) { + ESP_LOGW(TAG, "failed to set HT20 fallback, rc=0x%x", (unsigned)bw_rc); + } else { + ESP_LOGI(TAG, "Wi-Fi bandwidth set to HT20"); + } +} + static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, @@ -78,6 +102,13 @@ static esp_err_t start_sta_mode(void) { if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "connected to AP SSID:%s", CONFIG_TQ_WIFI_SSID); + wifi_manager_apply_sta_throughput_profile(); + esp_err_t ps_rc = esp_wifi_set_ps(WIFI_PS_NONE); + if (ps_rc == ESP_OK) { + ESP_LOGI(TAG, "Wi-Fi power save disabled (WIFI_PS_NONE)"); + } else { + ESP_LOGW(TAG, "failed to set WIFI_PS_NONE, rc=0x%x", (unsigned)ps_rc); + } s_ready = true; return ESP_OK; } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 883fbf0..f3c6702 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -6,8 +6,24 @@ CONFIG_BT_NIMBLE_ROLE_CENTRAL=y CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=247 # CONFIG_ESP_WIFI_SOFTAP_SUPPORT is not set CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=n +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=24 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=256 +CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=64 +CONFIG_ESP_WIFI_TX_BA_WIN=16 +CONFIG_ESP_WIFI_RX_BA_WIN=32 +CONFIG_LWIP_WND_SCALE=y +CONFIG_LWIP_TCP_RCV_SCALE=3 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=57344 +CONFIG_LWIP_TCP_WND_DEFAULT=262144 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=192 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=256 CONFIG_TQ_WIFI_SSID="TalkingQ" CONFIG_TQ_WIFI_PASSWORD="TalkingQ123" +CONFIG_TQ_Z_IMAGE_DEFAULT_SIZE="512*768" +CONFIG_TQ_Z_IMAGE_HTTP_BUFFER_BYTES=16384 +CONFIG_TQ_Z_IMAGE_HTTP_READ_CHUNK_BYTES=16384 +CONFIG_TQ_Z_IMAGE_COEX_PREFER_WIFI_DURING_DOWNLOAD=y # CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"