mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-09-01 16:48:23 +00:00
Fix tile download TLS memory and map rendering performance
- Route mbedtls allocations to PSRAM via mbedtls_platform_set_calloc_free() to fix TLS handshake failure (-32512 SSL alloc) with only ~36KB internal heap - Use WiFiClientSecure with setInsecure() for HTTPS tile downloads from OSM - Move tile downloads to background FreeRTOS task to avoid blocking UI thread - Add incremental tile loading (one PNG decode per update cycle) to prevent LVGL mutex timeout from decoding 4 tiles synchronously - Enable LVGL image cache (LV_IMG_CACHE_DEF_SIZE=8) so decoded PNGs stay in PSRAM and don't re-decode on every redraw - Add touch drag panning for map navigation via LV_EVENT_PRESSING Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1a89466b66
commit
0d3e1bae30
+1
-1
@@ -54,7 +54,7 @@
|
||||
#define LV_DRAW_COMPLEX 1
|
||||
#define LV_SHADOW_CACHE_SIZE 0
|
||||
#define LV_CIRCLE_CACHE_SIZE 4
|
||||
#define LV_IMG_CACHE_DEF_SIZE 0
|
||||
#define LV_IMG_CACHE_DEF_SIZE 8
|
||||
#define LV_GRADIENT_MAX_STOPS 2
|
||||
#define LV_GRAD_CACHE_DEF_SIZE 0
|
||||
#define LV_DITHER_GRADIENT 0
|
||||
|
||||
@@ -8,8 +8,30 @@
|
||||
#include "SDAccess.h"
|
||||
#include <SD.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <Log.h>
|
||||
#include <mbedtls/platform.h>
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
// ESP-IDF's default mbedtls allocator only uses internal SRAM (~36KB free),
|
||||
// which is too small for TLS handshake (~40-50KB). Redirect to PSRAM.
|
||||
// ESP32-S3's unified cache makes PSRAM safe for crypto operations.
|
||||
static void* psram_calloc(size_t n, size_t size) {
|
||||
void* ptr = heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (!ptr) {
|
||||
ptr = calloc(n, size); // Fallback to default allocator
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static bool _tls_allocator_set = false;
|
||||
|
||||
static void ensure_psram_tls() {
|
||||
if (_tls_allocator_set) return;
|
||||
mbedtls_platform_set_calloc_free(psram_calloc, free);
|
||||
_tls_allocator_set = true;
|
||||
}
|
||||
|
||||
namespace Hardware {
|
||||
namespace TDeck {
|
||||
@@ -61,26 +83,37 @@ bool TileDownloader::create_tile_dirs(int z, int x) {
|
||||
}
|
||||
|
||||
bool TileDownloader::download_tile(int z, int x, int y) {
|
||||
char log_buf[128];
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
WARNING("TileDownloader: WiFi not connected, skipping download");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SDAccess::is_ready()) {
|
||||
WARNING("TileDownloader: SD card not ready, skipping download");
|
||||
return false;
|
||||
}
|
||||
|
||||
String url = build_url(z, x, y);
|
||||
String sd_path = build_sd_path(z, x, y);
|
||||
|
||||
char log_buf[128];
|
||||
snprintf(log_buf, sizeof(log_buf), "TileDownloader: Fetching tile %d/%d/%d", z, x, y);
|
||||
DEBUG(log_buf);
|
||||
snprintf(log_buf, sizeof(log_buf), "TileDownloader: Downloading tile %d/%d/%d", z, x, y);
|
||||
INFO(log_buf);
|
||||
|
||||
// Route mbedtls allocations to PSRAM (one-time, persists for all future TLS)
|
||||
ensure_psram_tls();
|
||||
|
||||
WiFiClientSecure secureClient;
|
||||
secureClient.setInsecure();
|
||||
secureClient.setHandshakeTimeout(10);
|
||||
|
||||
HTTPClient http;
|
||||
http.setUserAgent("Pyxis/1.0 (ESP32; LXMF messenger)");
|
||||
http.setTimeout(10000);
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
|
||||
if (!http.begin(url)) {
|
||||
if (!http.begin(secureClient, url)) {
|
||||
WARNING("TileDownloader: Failed to begin HTTP connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,16 @@ MapScreen::MapScreen(lv_obj_t* parent)
|
||||
memset(_peer_labels, 0, sizeof(_peer_labels));
|
||||
memset(_loaded_tile_x, -1, sizeof(_loaded_tile_x));
|
||||
memset(_loaded_tile_y, -1, sizeof(_loaded_tile_y));
|
||||
memset(_pending_tiles, 0, sizeof(_pending_tiles));
|
||||
_pending_count = 0;
|
||||
_last_touch_x = 0;
|
||||
_last_touch_y = 0;
|
||||
|
||||
// Create download queue and background task
|
||||
_download_queue = xQueueCreate(8, sizeof(TileRequest));
|
||||
_download_complete = false;
|
||||
_download_task = nullptr;
|
||||
xTaskCreatePinnedToCore(download_task_func, "tile_dl", 16384, this, 1, &_download_task, 0);
|
||||
|
||||
LVGL_LOCK();
|
||||
|
||||
@@ -150,6 +160,10 @@ void MapScreen::create_viewport() {
|
||||
// Register key event on viewport for pan/zoom
|
||||
lv_obj_add_flag(_viewport, LV_OBJ_FLAG_CLICKABLE);
|
||||
lv_obj_add_event_cb(_viewport, on_key_event, LV_EVENT_KEY, this);
|
||||
|
||||
// Touch drag for panning
|
||||
lv_obj_add_event_cb(_viewport, on_touch_event, LV_EVENT_PRESSED, this);
|
||||
lv_obj_add_event_cb(_viewport, on_touch_event, LV_EVENT_PRESSING, this);
|
||||
}
|
||||
|
||||
void MapScreen::show() {
|
||||
@@ -186,6 +200,23 @@ void MapScreen::hide() {
|
||||
}
|
||||
|
||||
void MapScreen::update_gps_position() {
|
||||
// Reload tiles after background downloads complete
|
||||
if (_download_complete) {
|
||||
_download_complete = false;
|
||||
_loaded_zoom = -1; // Force tile reload
|
||||
update_tiles();
|
||||
}
|
||||
|
||||
// Incremental tile loading: process one pending tile per cycle
|
||||
// This prevents LVGL mutex timeout from decoding multiple PNGs at once
|
||||
if (_pending_count > 0) {
|
||||
PendingTile& pt = _pending_tiles[_pending_count - 1];
|
||||
load_tile(pt.slot, pt.x, pt.y, pt.z);
|
||||
_loaded_tile_x[pt.slot] = pt.x;
|
||||
_loaded_tile_y[pt.slot] = pt.y;
|
||||
_pending_count--;
|
||||
}
|
||||
|
||||
if (!_gps) return;
|
||||
|
||||
if (_gps->location.isValid()) {
|
||||
@@ -261,6 +292,8 @@ void MapScreen::update_tiles() {
|
||||
{base_tile_x + 1, base_tile_y + 1}
|
||||
};
|
||||
|
||||
_pending_count = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int col = i % 2;
|
||||
int row = i / 2;
|
||||
@@ -272,9 +305,22 @@ void MapScreen::update_tiles() {
|
||||
if (_loaded_zoom != _zoom ||
|
||||
_loaded_tile_x[i] != tile_coords[i][0] ||
|
||||
_loaded_tile_y[i] != tile_coords[i][1]) {
|
||||
load_tile(i, tile_coords[i][0], tile_coords[i][1], _zoom);
|
||||
_loaded_tile_x[i] = tile_coords[i][0];
|
||||
_loaded_tile_y[i] = tile_coords[i][1];
|
||||
|
||||
int tx = tile_coords[i][0];
|
||||
int ty = tile_coords[i][1];
|
||||
|
||||
if (Hardware::TDeck::TileDownloader::tile_exists(_zoom, tx, ty)) {
|
||||
// Tile on SD — load immediately (fast with LVGL image cache)
|
||||
load_tile(i, tx, ty, _zoom);
|
||||
_loaded_tile_x[i] = tx;
|
||||
_loaded_tile_y[i] = ty;
|
||||
} else {
|
||||
// Queue download + deferred load
|
||||
_pending_tiles[_pending_count++] = {i, _zoom, tx, ty};
|
||||
TileRequest req = {_zoom, tx, ty};
|
||||
xQueueSend(_download_queue, &req, 0);
|
||||
lv_img_set_src(_tile_imgs[i], "");
|
||||
}
|
||||
}
|
||||
}
|
||||
_loaded_zoom = _zoom;
|
||||
@@ -341,16 +387,46 @@ void MapScreen::pan(int dx, int dy) {
|
||||
}
|
||||
|
||||
void MapScreen::load_tile(int slot, int tile_x, int tile_y, int z) {
|
||||
// Ensure tile is on SD (download if missing and WiFi available)
|
||||
Hardware::TDeck::TileDownloader::ensure_tile(z, tile_x, tile_y);
|
||||
|
||||
// Build LVGL FS path: "S:tiles/{z}/{x}/{y}.png"
|
||||
char path[64];
|
||||
snprintf(path, sizeof(path), "S:tiles/%d/%d/%d.png", z, tile_x, tile_y);
|
||||
|
||||
lv_img_set_src(_tile_imgs[slot], path);
|
||||
}
|
||||
|
||||
void MapScreen::download_task_func(void* param) {
|
||||
MapScreen* self = (MapScreen*)param;
|
||||
TileRequest req;
|
||||
while (true) {
|
||||
if (xQueueReceive(self->_download_queue, &req, portMAX_DELAY) == pdTRUE) {
|
||||
if (Hardware::TDeck::TileDownloader::download_tile(req.z, req.x, req.y)) {
|
||||
self->_download_complete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapScreen::on_touch_event(lv_event_t* event) {
|
||||
MapScreen* screen = (MapScreen*)lv_event_get_user_data(event);
|
||||
lv_event_code_t code = lv_event_get_code(event);
|
||||
lv_indev_t* indev = lv_indev_get_act();
|
||||
if (!indev) return;
|
||||
|
||||
lv_point_t point;
|
||||
lv_indev_get_point(indev, &point);
|
||||
|
||||
if (code == LV_EVENT_PRESSED) {
|
||||
screen->_last_touch_x = point.x;
|
||||
screen->_last_touch_y = point.y;
|
||||
} else if (code == LV_EVENT_PRESSING) {
|
||||
int dx = screen->_last_touch_x - point.x;
|
||||
int dy = screen->_last_touch_y - point.y;
|
||||
if (dx != 0 || dy != 0) {
|
||||
screen->pan(dx, dy);
|
||||
screen->_last_touch_x = point.x;
|
||||
screen->_last_touch_y = point.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapScreen::on_back_clicked(lv_event_t* event) {
|
||||
MapScreen* screen = (MapScreen*)lv_event_get_user_data(event);
|
||||
if (screen->_back_callback) {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <lvgl.h>
|
||||
#include <functional>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include "Bytes.h"
|
||||
|
||||
class TinyGPSPlus;
|
||||
@@ -107,11 +109,33 @@ private:
|
||||
// Load a tile image into one of the 4 slots
|
||||
void load_tile(int slot, int tile_x, int tile_y, int z);
|
||||
|
||||
// Touch drag state
|
||||
int _last_touch_x;
|
||||
int _last_touch_y;
|
||||
|
||||
// Incremental tile loading — one tile per update cycle to avoid LVGL mutex timeout
|
||||
struct PendingTile {
|
||||
int slot, z, x, y;
|
||||
};
|
||||
PendingTile _pending_tiles[4];
|
||||
int _pending_count;
|
||||
|
||||
// Async tile downloading
|
||||
struct TileRequest {
|
||||
int z, x, y;
|
||||
};
|
||||
QueueHandle_t _download_queue;
|
||||
TaskHandle_t _download_task;
|
||||
volatile bool _download_complete;
|
||||
|
||||
static void download_task_func(void* param);
|
||||
|
||||
// Event handlers
|
||||
static void on_back_clicked(lv_event_t* event);
|
||||
static void on_zoom_in_clicked(lv_event_t* event);
|
||||
static void on_zoom_out_clicked(lv_event_t* event);
|
||||
static void on_key_event(lv_event_t* event);
|
||||
static void on_touch_event(lv_event_t* event);
|
||||
};
|
||||
|
||||
} // namespace LXMF
|
||||
|
||||
Reference in New Issue
Block a user