Enhance memory management in asset download and OTA processes by repl… (#1716)

* Enhance memory management in asset download and OTA processes by replacing static buffer allocations with dynamic memory allocation using heap capabilities. Update SPIRAM configuration values for improved memory usage. Add logging for error handling in buffer allocation failures. Introduce a new parameter in CloseAudioChannel to control goodbye message sending in MQTT and WebSocket protocols.

* Update component versions in idf_component.yml and refactor GIF decoder functions for improved performance. Bump versions for audio effects, audio codec, LED strip, and other dependencies. Change GIF read and seek functions to inline for optimization.

* Update language files to include new phrases for flight mode and connection status across multiple locales. Added translations for "FLIGHT_MODE_ON", "FLIGHT_MODE_OFF", "CONNECTION_SUCCESSFUL", and "MODEM_INIT_ERROR" in various languages, enhancing user experience and localization support.

* fix wechat display
This commit is contained in:
Xiaoxia
2026-01-31 22:58:08 +08:00
committed by GitHub
parent 96f34ec70f
commit f7284a57df
50 changed files with 277 additions and 125 deletions

View File

@ -12,6 +12,7 @@
#include <esp_log.h> #include <esp_log.h>
#include <esp_timer.h> #include <esp_timer.h>
#include <esp_heap_caps.h>
#include <cbin_font.h> #include <cbin_font.h>
@ -464,16 +465,21 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
SECTOR_SIZE, content_length, sectors_to_erase, total_erase_size); SECTOR_SIZE, content_length, sectors_to_erase, total_erase_size);
// 写入新的资源文件到分区一边erase一边写入 // 写入新的资源文件到分区一边erase一边写入
char buffer[512]; char* buffer = (char*)heap_caps_malloc(SECTOR_SIZE, MALLOC_CAP_INTERNAL);
if (buffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate buffer");
return false;
}
size_t total_written = 0; size_t total_written = 0;
size_t recent_written = 0; size_t recent_written = 0;
size_t current_sector = 0; size_t current_sector = 0;
auto last_calc_time = esp_timer_get_time(); auto last_calc_time = esp_timer_get_time();
while (true) { while (true) {
int ret = http->Read(buffer, sizeof(buffer)); int ret = http->Read(buffer, SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret)); ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret));
heap_caps_free(buffer);
return false; return false;
} }
@ -493,6 +499,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
// 确保擦除范围不超过分区大小 // 确保擦除范围不超过分区大小
if (sector_end > partition_->size) { if (sector_end > partition_->size) {
ESP_LOGE(TAG, "Sector end (%u) exceeds partition size (%lu)", sector_end, partition_->size); ESP_LOGE(TAG, "Sector end (%u) exceeds partition size (%lu)", sector_end, partition_->size);
heap_caps_free(buffer);
return false; return false;
} }
@ -500,6 +507,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
esp_err_t err = esp_partition_erase_range(partition_, sector_start, SECTOR_SIZE); esp_err_t err = esp_partition_erase_range(partition_, sector_start, SECTOR_SIZE);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase sector %u at offset %u: %s", current_sector, sector_start, esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to erase sector %u at offset %u: %s", current_sector, sector_start, esp_err_to_name(err));
heap_caps_free(buffer);
return false; return false;
} }
@ -510,6 +518,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
esp_err_t err = esp_partition_write(partition_, total_written, buffer, ret); esp_err_t err = esp_partition_write(partition_, total_written, buffer, ret);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write to assets partition at offset %u: %s", total_written, esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to write to assets partition at offset %u: %s", total_written, esp_err_to_name(err));
heap_caps_free(buffer);
return false; return false;
} }
@ -531,6 +540,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
} }
http->Close(); http->Close();
heap_caps_free(buffer);
if (total_written != content_length) { if (total_written != content_length) {
ESP_LOGE(TAG, "Downloaded size (%u) does not match expected size (%u)", total_written, content_length); ESP_LOGE(TAG, "Downloaded size (%u) does not match expected size (%u)", total_written, content_length);

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "جاري تحميل الموارد...", "LOADING_ASSETS": "جاري تحميل الموارد...",
"PLEASE_WAIT": "يرجى الانتظار...", "PLEASE_WAIT": "يرجى الانتظار...",
"FOUND_NEW_ASSETS": "تم العثور على موارد جديدة: %s", "FOUND_NEW_ASSETS": "تم العثور على موارد جديدة: %s",
"HELLO_MY_FRIEND": "مرحباً، صديقي!" "HELLO_MY_FRIEND": "مرحباً، صديقي!",
"CONNECTION_SUCCESSFUL": "تم الاتصال بنجاح",
"FLIGHT_MODE_OFF": "وضع الطيران معطل",
"FLIGHT_MODE_ON": "وضع الطيران قيد التشغيل",
"MODEM_INIT_ERROR": "فشل تهيئة المودم"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Намерени нови ресурси: %s", "FOUND_NEW_ASSETS": "Намерени нови ресурси: %s",
"DOWNLOAD_ASSETS_FAILED": "Неуспешно изтегляне на ресурси", "DOWNLOAD_ASSETS_FAILED": "Неуспешно изтегляне на ресурси",
"LOADING_ASSETS": "Зареждане на ресурси...", "LOADING_ASSETS": "Зареждане на ресурси...",
"HELLO_MY_FRIEND": "Здравей, мой приятел!" "HELLO_MY_FRIEND": "Здравей, мой приятел!",
"FLIGHT_MODE_OFF": "Режим на самолет е изключен",
"FLIGHT_MODE_ON": "Режим на самолет е включен",
"MODEM_INIT_ERROR": "Неуспешна инициализация на модема"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "S'han trobat nous recursos: %s", "FOUND_NEW_ASSETS": "S'han trobat nous recursos: %s",
"DOWNLOAD_ASSETS_FAILED": "No s'han pogut descarregar els recursos", "DOWNLOAD_ASSETS_FAILED": "No s'han pogut descarregar els recursos",
"LOADING_ASSETS": "Carregant recursos...", "LOADING_ASSETS": "Carregant recursos...",
"HELLO_MY_FRIEND": "Hola, amic meu!" "HELLO_MY_FRIEND": "Hola, amic meu!",
"FLIGHT_MODE_OFF": "El mode avió està desactivat",
"FLIGHT_MODE_ON": "El mode avió està activat",
"MODEM_INIT_ERROR": "Error d'inicialització del mòdem"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Načítání prostředků...", "LOADING_ASSETS": "Načítání prostředků...",
"PLEASE_WAIT": "Prosím čekejte...", "PLEASE_WAIT": "Prosím čekejte...",
"FOUND_NEW_ASSETS": "Nalezeny nové prostředky: %s", "FOUND_NEW_ASSETS": "Nalezeny nové prostředky: %s",
"HELLO_MY_FRIEND": "Ahoj, můj příteli!" "HELLO_MY_FRIEND": "Ahoj, můj příteli!",
"CONNECTION_SUCCESSFUL": "Připojení úspěšné",
"FLIGHT_MODE_OFF": "Letecký režim je vypnutý",
"FLIGHT_MODE_ON": "Letecký režim je zapnutý",
"MODEM_INIT_ERROR": "Chyba inicializace modemu"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Fandt nye ressourcer: %s", "FOUND_NEW_ASSETS": "Fandt nye ressourcer: %s",
"DOWNLOAD_ASSETS_FAILED": "Download af ressourcer mislykkedes", "DOWNLOAD_ASSETS_FAILED": "Download af ressourcer mislykkedes",
"LOADING_ASSETS": "Indlæser ressourcer...", "LOADING_ASSETS": "Indlæser ressourcer...",
"HELLO_MY_FRIEND": "Hej, min ven!" "HELLO_MY_FRIEND": "Hej, min ven!",
"FLIGHT_MODE_OFF": "Flytilstand er slukket",
"FLIGHT_MODE_ON": "Flytilstand er tændt",
"MODEM_INIT_ERROR": "Modeminitialisering mislykkedes"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Ressourcen werden geladen...", "LOADING_ASSETS": "Ressourcen werden geladen...",
"PLEASE_WAIT": "Bitte warten...", "PLEASE_WAIT": "Bitte warten...",
"FOUND_NEW_ASSETS": "Neue Ressourcen gefunden: %s", "FOUND_NEW_ASSETS": "Neue Ressourcen gefunden: %s",
"HELLO_MY_FRIEND": "Hallo, mein Freund!" "HELLO_MY_FRIEND": "Hallo, mein Freund!",
"CONNECTION_SUCCESSFUL": "Verbindung erfolgreich",
"FLIGHT_MODE_OFF": "Flugmodus ist deaktiviert",
"FLIGHT_MODE_ON": "Flugmodus ist aktiviert",
"MODEM_INIT_ERROR": "Modem-Initialisierung fehlgeschlagen"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Βρέθηκαν νέοι πόροι: %s", "FOUND_NEW_ASSETS": "Βρέθηκαν νέοι πόροι: %s",
"DOWNLOAD_ASSETS_FAILED": "Αποτυχία λήψης πόρων", "DOWNLOAD_ASSETS_FAILED": "Αποτυχία λήψης πόρων",
"LOADING_ASSETS": "Φόρτωση πόρων...", "LOADING_ASSETS": "Φόρτωση πόρων...",
"HELLO_MY_FRIEND": "Γεια σου, φίλε μου!" "HELLO_MY_FRIEND": "Γεια σου, φίλε μου!",
"FLIGHT_MODE_OFF": "Η λειτουργία πτήσης είναι απενεργοποιημένη",
"FLIGHT_MODE_ON": "Η λειτουργία πτήσης είναι ενεργή",
"MODEM_INIT_ERROR": "Αποτυχία αρχικοποίησης modem"
} }
} }

View File

@ -13,6 +13,8 @@
"REG_ERROR": "Unable to access network, please check SIM card status", "REG_ERROR": "Unable to access network, please check SIM card status",
"MODEM_INIT_ERROR": "Modem initialization failed", "MODEM_INIT_ERROR": "Modem initialization failed",
"DETECTING_MODULE": "Detecting module...", "DETECTING_MODULE": "Detecting module...",
"FLIGHT_MODE_ON": "Flight mode is on",
"FLIGHT_MODE_OFF": "Flight mode is off",
"REGISTERING_NETWORK": "Waiting for network...", "REGISTERING_NETWORK": "Waiting for network...",
"CHECKING_NEW_VERSION": "Checking for new version...", "CHECKING_NEW_VERSION": "Checking for new version...",
"CHECK_NEW_VERSION_FAILED": "Check for new version failed, will retry in %d seconds: %s", "CHECK_NEW_VERSION_FAILED": "Check for new version failed, will retry in %d seconds: %s",

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Cargando recursos...", "LOADING_ASSETS": "Cargando recursos...",
"PLEASE_WAIT": "Por favor espere...", "PLEASE_WAIT": "Por favor espere...",
"FOUND_NEW_ASSETS": "Encontrados nuevos recursos: %s", "FOUND_NEW_ASSETS": "Encontrados nuevos recursos: %s",
"HELLO_MY_FRIEND": "¡Hola, mi amigo!" "HELLO_MY_FRIEND": "¡Hola, mi amigo!",
"CONNECTION_SUCCESSFUL": "Conexión exitosa",
"FLIGHT_MODE_OFF": "El modo avión está desactivado",
"FLIGHT_MODE_ON": "El modo avión está activado",
"MODEM_INIT_ERROR": "Error de inicialización del módem"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "منابع جدید یافت شد: %s", "FOUND_NEW_ASSETS": "منابع جدید یافت شد: %s",
"DOWNLOAD_ASSETS_FAILED": "دانلود منابع ناموفق بود", "DOWNLOAD_ASSETS_FAILED": "دانلود منابع ناموفق بود",
"LOADING_ASSETS": "بارگذاری منابع...", "LOADING_ASSETS": "بارگذاری منابع...",
"HELLO_MY_FRIEND": "سلام، دوست من!" "HELLO_MY_FRIEND": "سلام، دوست من!",
"FLIGHT_MODE_OFF": "حالت پرواز خاموش است",
"FLIGHT_MODE_ON": "حالت پرواز روشن است",
"MODEM_INIT_ERROR": "خطا در راه‌اندازی مودم"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Ladataan resursseja...", "LOADING_ASSETS": "Ladataan resursseja...",
"PLEASE_WAIT": "Odota hetki...", "PLEASE_WAIT": "Odota hetki...",
"FOUND_NEW_ASSETS": "Löydetty uusia resursseja: %s", "FOUND_NEW_ASSETS": "Löydetty uusia resursseja: %s",
"HELLO_MY_FRIEND": "Hei, ystäväni!" "HELLO_MY_FRIEND": "Hei, ystäväni!",
"CONNECTION_SUCCESSFUL": "Yhteys onnistui",
"FLIGHT_MODE_OFF": "Lentotila on pois päältä",
"FLIGHT_MODE_ON": "Lentotila on päällä",
"MODEM_INIT_ERROR": "Modeemin alustus epäonnistui"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Nakahanap ng mga bagong assets: %s", "FOUND_NEW_ASSETS": "Nakahanap ng mga bagong assets: %s",
"DOWNLOAD_ASSETS_FAILED": "Nabigo ang pag-download ng mga assets", "DOWNLOAD_ASSETS_FAILED": "Nabigo ang pag-download ng mga assets",
"LOADING_ASSETS": "Nilo-load ang mga assets...", "LOADING_ASSETS": "Nilo-load ang mga assets...",
"HELLO_MY_FRIEND": "Kumusta, kaibigan ko!" "HELLO_MY_FRIEND": "Kumusta, kaibigan ko!",
"FLIGHT_MODE_OFF": "Naka-off ang flight mode",
"FLIGHT_MODE_ON": "Naka-on ang flight mode",
"MODEM_INIT_ERROR": "Nabigo ang pag-initialize ng modem"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Chargement des ressources...", "LOADING_ASSETS": "Chargement des ressources...",
"PLEASE_WAIT": "Veuillez patienter...", "PLEASE_WAIT": "Veuillez patienter...",
"FOUND_NEW_ASSETS": "Nouvelles ressources trouvées: %s", "FOUND_NEW_ASSETS": "Nouvelles ressources trouvées: %s",
"HELLO_MY_FRIEND": "Bonjour, mon ami !" "HELLO_MY_FRIEND": "Bonjour, mon ami !",
"CONNECTION_SUCCESSFUL": "Connexion réussie",
"FLIGHT_MODE_OFF": "Le mode avion est désactivé",
"FLIGHT_MODE_ON": "Le mode avion est activé",
"MODEM_INIT_ERROR": "Échec de l'initialisation du modem"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "נמצאו משאבים חדשים: %s", "FOUND_NEW_ASSETS": "נמצאו משאבים חדשים: %s",
"DOWNLOAD_ASSETS_FAILED": "הורדת משאבים נכשלה", "DOWNLOAD_ASSETS_FAILED": "הורדת משאבים נכשלה",
"LOADING_ASSETS": "טוען משאבים...", "LOADING_ASSETS": "טוען משאבים...",
"HELLO_MY_FRIEND": "שלום, ידידי!" "HELLO_MY_FRIEND": "שלום, ידידי!",
"FLIGHT_MODE_OFF": "מצב טיסה כבוי",
"FLIGHT_MODE_ON": "מצב טיסה מופעל",
"MODEM_INIT_ERROR": "אתחול המודם נכשל"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "संसाधन लोड हो रहे हैं...", "LOADING_ASSETS": "संसाधन लोड हो रहे हैं...",
"PLEASE_WAIT": "कृपया प्रतीक्षा करें...", "PLEASE_WAIT": "कृपया प्रतीक्षा करें...",
"FOUND_NEW_ASSETS": "नए संसाधन मिले: %s", "FOUND_NEW_ASSETS": "नए संसाधन मिले: %s",
"HELLO_MY_FRIEND": "नमस्ते, मेरे दोस्त!" "HELLO_MY_FRIEND": "नमस्ते, मेरे दोस्त!",
"CONNECTION_SUCCESSFUL": "कनेक्शन सफल",
"FLIGHT_MODE_OFF": "फ़्लाइट मोड बंद है",
"FLIGHT_MODE_ON": "फ़्लाइट मोड चालू है",
"MODEM_INIT_ERROR": "मॉडेम आरंभीकरण विफल"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Pronađeni novi resursi: %s", "FOUND_NEW_ASSETS": "Pronađeni novi resursi: %s",
"DOWNLOAD_ASSETS_FAILED": "Preuzimanje resursa nije uspjelo", "DOWNLOAD_ASSETS_FAILED": "Preuzimanje resursa nije uspjelo",
"LOADING_ASSETS": "Učitavanje resursa...", "LOADING_ASSETS": "Učitavanje resursa...",
"HELLO_MY_FRIEND": "Bok, moj prijatelju!" "HELLO_MY_FRIEND": "Bok, moj prijatelju!",
"FLIGHT_MODE_OFF": "Način rada u zrakoplovu je isključen",
"FLIGHT_MODE_ON": "Način rada u zrakoplovu je uključen",
"MODEM_INIT_ERROR": "Neuspjela inicijalizacija modema"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Új erőforrások találva: %s", "FOUND_NEW_ASSETS": "Új erőforrások találva: %s",
"DOWNLOAD_ASSETS_FAILED": "Az erőforrások letöltése sikertelen", "DOWNLOAD_ASSETS_FAILED": "Az erőforrások letöltése sikertelen",
"LOADING_ASSETS": "Erőforrások betöltése...", "LOADING_ASSETS": "Erőforrások betöltése...",
"HELLO_MY_FRIEND": "Helló, barátom!" "HELLO_MY_FRIEND": "Helló, barátom!",
"FLIGHT_MODE_OFF": "A repülési mód ki van kapcsolva",
"FLIGHT_MODE_ON": "A repülési mód be van kapcsolva",
"MODEM_INIT_ERROR": "A modem inicializálása sikertelen"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Memuat aset...", "LOADING_ASSETS": "Memuat aset...",
"PLEASE_WAIT": "Mohon tunggu...", "PLEASE_WAIT": "Mohon tunggu...",
"FOUND_NEW_ASSETS": "Ditemukan aset baru: %s", "FOUND_NEW_ASSETS": "Ditemukan aset baru: %s",
"HELLO_MY_FRIEND": "Halo, teman saya!" "HELLO_MY_FRIEND": "Halo, teman saya!",
"CONNECTION_SUCCESSFUL": "Koneksi berhasil",
"FLIGHT_MODE_OFF": "Mode pesawat nonaktif",
"FLIGHT_MODE_ON": "Mode pesawat aktif",
"MODEM_INIT_ERROR": "Gagal menginisialisasi modem"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Caricamento risorse...", "LOADING_ASSETS": "Caricamento risorse...",
"PLEASE_WAIT": "Attendere prego...", "PLEASE_WAIT": "Attendere prego...",
"FOUND_NEW_ASSETS": "Trovate nuove risorse: %s", "FOUND_NEW_ASSETS": "Trovate nuove risorse: %s",
"HELLO_MY_FRIEND": "Ciao, amico mio!" "HELLO_MY_FRIEND": "Ciao, amico mio!",
"CONNECTION_SUCCESSFUL": "Connessione riuscita",
"FLIGHT_MODE_OFF": "La modalità aereo è disattivata",
"FLIGHT_MODE_ON": "La modalità aereo è attiva",
"MODEM_INIT_ERROR": "Inizializzazione modem non riuscita"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "アセットを読み込み中...", "LOADING_ASSETS": "アセットを読み込み中...",
"PLEASE_WAIT": "お待ちください...", "PLEASE_WAIT": "お待ちください...",
"FOUND_NEW_ASSETS": "新しいアセットが見つかりました: %s", "FOUND_NEW_ASSETS": "新しいアセットが見つかりました: %s",
"HELLO_MY_FRIEND": "こんにちは、友達!" "HELLO_MY_FRIEND": "こんにちは、友達!",
"CONNECTION_SUCCESSFUL": "接続成功",
"FLIGHT_MODE_OFF": "機内モードがオフです",
"FLIGHT_MODE_ON": "機内モードがオンです",
"MODEM_INIT_ERROR": "モデムの初期化に失敗しました"
} }
} }

View File

@ -51,6 +51,9 @@
"LOADING_ASSETS": "에셋 로딩 중...", "LOADING_ASSETS": "에셋 로딩 중...",
"PLEASE_WAIT": "잠시 기다려 주세요...", "PLEASE_WAIT": "잠시 기다려 주세요...",
"FOUND_NEW_ASSETS": "새로운 에셋을 발견했습니다: %s", "FOUND_NEW_ASSETS": "새로운 에셋을 발견했습니다: %s",
"HELLO_MY_FRIEND": "안녕하세요, 친구!" "HELLO_MY_FRIEND": "안녕하세요, 친구!",
"FLIGHT_MODE_OFF": "비행기 모드가 꺼져 있습니다",
"FLIGHT_MODE_ON": "비행기 모드가 켜져 있습니다",
"MODEM_INIT_ERROR": "모뎀 초기화 실패"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Menemui aset baharu: %s", "FOUND_NEW_ASSETS": "Menemui aset baharu: %s",
"DOWNLOAD_ASSETS_FAILED": "Gagal memuat turun aset", "DOWNLOAD_ASSETS_FAILED": "Gagal memuat turun aset",
"LOADING_ASSETS": "Memuatkan aset...", "LOADING_ASSETS": "Memuatkan aset...",
"HELLO_MY_FRIEND": "Hai, kawan saya!" "HELLO_MY_FRIEND": "Hai, kawan saya!",
"FLIGHT_MODE_OFF": "Mod penerbangan dimatikan",
"FLIGHT_MODE_ON": "Mod penerbangan dihidupkan",
"MODEM_INIT_ERROR": "Modem gagal dimulakan"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Fant nye ressurser: %s", "FOUND_NEW_ASSETS": "Fant nye ressurser: %s",
"DOWNLOAD_ASSETS_FAILED": "Nedlasting av ressurser mislyktes", "DOWNLOAD_ASSETS_FAILED": "Nedlasting av ressurser mislyktes",
"LOADING_ASSETS": "Laster ressurser...", "LOADING_ASSETS": "Laster ressurser...",
"HELLO_MY_FRIEND": "Hei, min venn!" "HELLO_MY_FRIEND": "Hei, min venn!",
"FLIGHT_MODE_OFF": "Flymodus er av",
"FLIGHT_MODE_ON": "Flymodus er på",
"MODEM_INIT_ERROR": "Modeminitialisering mislyktes"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Nieuwe bronnen gevonden: %s", "FOUND_NEW_ASSETS": "Nieuwe bronnen gevonden: %s",
"DOWNLOAD_ASSETS_FAILED": "Downloaden van bronnen mislukt", "DOWNLOAD_ASSETS_FAILED": "Downloaden van bronnen mislukt",
"LOADING_ASSETS": "Bronnen laden...", "LOADING_ASSETS": "Bronnen laden...",
"HELLO_MY_FRIEND": "Hallo, mijn vriend!" "HELLO_MY_FRIEND": "Hallo, mijn vriend!",
"FLIGHT_MODE_OFF": "Vliegtuigmodus is uitgeschakeld",
"FLIGHT_MODE_ON": "Vliegtuigmodus is ingeschakeld",
"MODEM_INIT_ERROR": "Modeminitialisatie mislukt"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Ładowanie zasobów...", "LOADING_ASSETS": "Ładowanie zasobów...",
"PLEASE_WAIT": "Proszę czekać...", "PLEASE_WAIT": "Proszę czekać...",
"FOUND_NEW_ASSETS": "Znaleziono nowe zasoby: %s", "FOUND_NEW_ASSETS": "Znaleziono nowe zasoby: %s",
"HELLO_MY_FRIEND": "Cześć, mój przyjacielu!" "HELLO_MY_FRIEND": "Cześć, mój przyjacielu!",
"CONNECTION_SUCCESSFUL": "Połączenie udane",
"FLIGHT_MODE_OFF": "Tryb samolotowy jest wyłączony",
"FLIGHT_MODE_ON": "Tryb samolotowy jest włączony",
"MODEM_INIT_ERROR": "Inicjalizacja modemu nie powiodła się"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "A carregar recursos...", "LOADING_ASSETS": "A carregar recursos...",
"PLEASE_WAIT": "Por favor aguarde...", "PLEASE_WAIT": "Por favor aguarde...",
"FOUND_NEW_ASSETS": "Encontrados novos recursos: %s", "FOUND_NEW_ASSETS": "Encontrados novos recursos: %s",
"HELLO_MY_FRIEND": "Olá, meu amigo!" "HELLO_MY_FRIEND": "Olá, meu amigo!",
"CONNECTION_SUCCESSFUL": "Ligação bem-sucedida",
"FLIGHT_MODE_OFF": "O modo avião está desativado",
"FLIGHT_MODE_ON": "O modo avião está ativado",
"MODEM_INIT_ERROR": "Falha na inicialização do modem"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Se încarcă resursele...", "LOADING_ASSETS": "Se încarcă resursele...",
"PLEASE_WAIT": "Vă rugăm să așteptați...", "PLEASE_WAIT": "Vă rugăm să așteptați...",
"FOUND_NEW_ASSETS": "S-au găsit resurse noi: %s", "FOUND_NEW_ASSETS": "S-au găsit resurse noi: %s",
"HELLO_MY_FRIEND": "Salut, prietenul meu!" "HELLO_MY_FRIEND": "Salut, prietenul meu!",
"CONNECTION_SUCCESSFUL": "Conexiune reușită",
"FLIGHT_MODE_OFF": "Modul avion este dezactivat",
"FLIGHT_MODE_ON": "Modul avion este activat",
"MODEM_INIT_ERROR": "Inițializarea modemului a eșuat"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Загрузка ресурсов...", "LOADING_ASSETS": "Загрузка ресурсов...",
"PLEASE_WAIT": "Пожалуйста, подождите...", "PLEASE_WAIT": "Пожалуйста, подождите...",
"FOUND_NEW_ASSETS": "Найдены новые ресурсы: %s", "FOUND_NEW_ASSETS": "Найдены новые ресурсы: %s",
"HELLO_MY_FRIEND": "Привет, мой друг!" "HELLO_MY_FRIEND": "Привет, мой друг!",
"CONNECTION_SUCCESSFUL": "Подключение успешно",
"FLIGHT_MODE_OFF": "Режим полета выключен",
"FLIGHT_MODE_ON": "Режим полета включен",
"MODEM_INIT_ERROR": "Ошибка инициализации модема"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Nájdené nové zdroje: %s", "FOUND_NEW_ASSETS": "Nájdené nové zdroje: %s",
"DOWNLOAD_ASSETS_FAILED": "Sťahovanie zdrojov zlyhalo", "DOWNLOAD_ASSETS_FAILED": "Sťahovanie zdrojov zlyhalo",
"LOADING_ASSETS": "Načítavanie zdrojov...", "LOADING_ASSETS": "Načítavanie zdrojov...",
"HELLO_MY_FRIEND": "Ahoj, môj priateľ!" "HELLO_MY_FRIEND": "Ahoj, môj priateľ!",
"FLIGHT_MODE_OFF": "Letecký režim je vypnutý",
"FLIGHT_MODE_ON": "Letecký režim je zapnutý",
"MODEM_INIT_ERROR": "Chyba inicializácie modemu"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Najdeni novi viri: %s", "FOUND_NEW_ASSETS": "Najdeni novi viri: %s",
"DOWNLOAD_ASSETS_FAILED": "Prenos virov ni uspel", "DOWNLOAD_ASSETS_FAILED": "Prenos virov ni uspel",
"LOADING_ASSETS": "Nalaganje virov...", "LOADING_ASSETS": "Nalaganje virov...",
"HELLO_MY_FRIEND": "Pozdravljeni, moj prijatelj!" "HELLO_MY_FRIEND": "Pozdravljeni, moj prijatelj!",
"FLIGHT_MODE_OFF": "Način leta je izklopljen",
"FLIGHT_MODE_ON": "Način leta je vklopljen",
"MODEM_INIT_ERROR": "Inicializacija modema ni uspela"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Пронађени нови ресурси: %s", "FOUND_NEW_ASSETS": "Пронађени нови ресурси: %s",
"DOWNLOAD_ASSETS_FAILED": "Преузимање ресурса није успело", "DOWNLOAD_ASSETS_FAILED": "Преузимање ресурса није успело",
"LOADING_ASSETS": "Учитавање ресурса...", "LOADING_ASSETS": "Учитавање ресурса...",
"HELLO_MY_FRIEND": "Здраво, пријатељу!" "HELLO_MY_FRIEND": "Здраво, пријатељу!",
"FLIGHT_MODE_OFF": "Режим лета је искључен",
"FLIGHT_MODE_ON": "Режим лета је укључен",
"MODEM_INIT_ERROR": "Иницијализација модема није успела"
} }
} }

View File

@ -51,7 +51,9 @@
"FOUND_NEW_ASSETS": "Hittade nya resurser: %s", "FOUND_NEW_ASSETS": "Hittade nya resurser: %s",
"DOWNLOAD_ASSETS_FAILED": "Nedladdning av resurser misslyckades", "DOWNLOAD_ASSETS_FAILED": "Nedladdning av resurser misslyckades",
"LOADING_ASSETS": "Laddar resurser...", "LOADING_ASSETS": "Laddar resurser...",
"HELLO_MY_FRIEND": "Hej, min vän!" "HELLO_MY_FRIEND": "Hej, min vän!",
"FLIGHT_MODE_OFF": "Flygläge är av",
"FLIGHT_MODE_ON": "Flygläge är på",
"MODEM_INIT_ERROR": "Modeminitiering misslyckades"
} }
} }

View File

@ -51,6 +51,9 @@
"LOADING_ASSETS": "กำลังโหลดทรัพยากร...", "LOADING_ASSETS": "กำลังโหลดทรัพยากร...",
"PLEASE_WAIT": "กรุณารอสักครู่...", "PLEASE_WAIT": "กรุณารอสักครู่...",
"FOUND_NEW_ASSETS": "พบทรัพยากรใหม่: %s", "FOUND_NEW_ASSETS": "พบทรัพยากรใหม่: %s",
"HELLO_MY_FRIEND": "สวัสดี เพื่อนของฉัน!" "HELLO_MY_FRIEND": "สวัสดี เพื่อนของฉัน!",
"FLIGHT_MODE_OFF": "โหมดเครื่องบินปิดอยู่",
"FLIGHT_MODE_ON": "โหมดเครื่องบินเปิดอยู่",
"MODEM_INIT_ERROR": "การเริ่มต้นโมเด็มล้มเหลว"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Varlıklar yükleniyor...", "LOADING_ASSETS": "Varlıklar yükleniyor...",
"PLEASE_WAIT": "Lütfen bekleyin...", "PLEASE_WAIT": "Lütfen bekleyin...",
"FOUND_NEW_ASSETS": "Yeni varlıklar bulundu: %s", "FOUND_NEW_ASSETS": "Yeni varlıklar bulundu: %s",
"HELLO_MY_FRIEND": "Merhaba, arkadaşım!" "HELLO_MY_FRIEND": "Merhaba, arkadaşım!",
"CONNECTION_SUCCESSFUL": "Bağlantı başarılı",
"FLIGHT_MODE_OFF": "Uçak modu kapalı",
"FLIGHT_MODE_ON": "Uçak modu açık",
"MODEM_INIT_ERROR": "Modem başlatma hatası"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "Завантаження ресурсів...", "LOADING_ASSETS": "Завантаження ресурсів...",
"PLEASE_WAIT": "Будь ласка, зачекайте...", "PLEASE_WAIT": "Будь ласка, зачекайте...",
"FOUND_NEW_ASSETS": "Знайдено нові ресурси: %s", "FOUND_NEW_ASSETS": "Знайдено нові ресурси: %s",
"HELLO_MY_FRIEND": "Привіт, мій друже!" "HELLO_MY_FRIEND": "Привіт, мій друже!",
"CONNECTION_SUCCESSFUL": "Підключення успішне",
"FLIGHT_MODE_OFF": "Режим польоту вимкнено",
"FLIGHT_MODE_ON": "Режим польоту увімкнено",
"MODEM_INIT_ERROR": "Помилка ініціалізації модему"
} }
} }

View File

@ -51,6 +51,9 @@
"LOADING_ASSETS": "Đang tải tài nguyên...", "LOADING_ASSETS": "Đang tải tài nguyên...",
"PLEASE_WAIT": "Vui lòng đợi...", "PLEASE_WAIT": "Vui lòng đợi...",
"FOUND_NEW_ASSETS": "Tìm thấy tài nguyên mới: %s", "FOUND_NEW_ASSETS": "Tìm thấy tài nguyên mới: %s",
"HELLO_MY_FRIEND": "Xin chào, bạn của tôi!" "HELLO_MY_FRIEND": "Xin chào, bạn của tôi!",
"FLIGHT_MODE_OFF": "Chế độ máy bay đang tắt",
"FLIGHT_MODE_ON": "Chế độ máy bay đang bật",
"MODEM_INIT_ERROR": "Khởi tạo modem thất bại"
} }
} }

View File

@ -51,6 +51,9 @@
"LOADING_ASSETS": "加载资源...", "LOADING_ASSETS": "加载资源...",
"PLEASE_WAIT": "请稍候...", "PLEASE_WAIT": "请稍候...",
"FOUND_NEW_ASSETS": "发现新资源: %s", "FOUND_NEW_ASSETS": "发现新资源: %s",
"HELLO_MY_FRIEND": "你好,我的朋友!" "HELLO_MY_FRIEND": "你好,我的朋友!",
"CONNECTION_SUCCESSFUL": "连接成功",
"FLIGHT_MODE_OFF": "飞行模式已关闭",
"FLIGHT_MODE_ON": "飞行模式已开启"
} }
} }

View File

@ -50,6 +50,10 @@
"LOADING_ASSETS": "載入資源...", "LOADING_ASSETS": "載入資源...",
"PLEASE_WAIT": "請稍候...", "PLEASE_WAIT": "請稍候...",
"FOUND_NEW_ASSETS": "發現新資源: %s", "FOUND_NEW_ASSETS": "發現新資源: %s",
"HELLO_MY_FRIEND": "你好,我的朋友!" "HELLO_MY_FRIEND": "你好,我的朋友!",
"CONNECTION_SUCCESSFUL": "連線成功",
"FLIGHT_MODE_OFF": "飛航模式已關閉",
"FLIGHT_MODE_ON": "飛航模式已開啟",
"MODEM_INIT_ERROR": "模組初始化失敗"
} }
} }

View File

@ -107,6 +107,9 @@ void Nt26Board::StartNetwork() {
ScheduleAsyncStop(); ScheduleAsyncStop();
OnNetworkEvent(NetworkEvent::ModemErrorInitFailed); OnNetworkEvent(NetworkEvent::ModemErrorInitFailed);
break; break;
case UartEthModem::UartEthModemEvent::InFlightMode:
ESP_LOGW(TAG, "Modem in flight mode");
break;
} }
}); });

View File

@ -12,6 +12,7 @@
#include <esp_lvgl_port.h> #include <esp_lvgl_port.h>
#include <esp_psram.h> #include <esp_psram.h>
#include <cstring> #include <cstring>
#include <src/misc/cache/lv_cache.h>
#include "board.h" #include "board.h"
@ -568,28 +569,25 @@ void LcdDisplay::SetChatMessage(const char* role, const char* content) {
lv_obj_t* msg_text = lv_label_create(msg_bubble); lv_obj_t* msg_text = lv_label_create(msg_bubble);
lv_label_set_text(msg_text, content); lv_label_set_text(msg_text, content);
// Calculate actual text width // Calculate bubble width constraints
lv_coord_t text_width = lv_txt_get_width(content, strlen(content), text_font, 0);
// Calculate bubble width
lv_coord_t max_width = LV_HOR_RES * 85 / 100 - 16; // 85% of screen width lv_coord_t max_width = LV_HOR_RES * 85 / 100 - 16; // 85% of screen width
lv_coord_t min_width = 20; lv_coord_t min_width = 20;
lv_coord_t bubble_width;
// Let LVGL calculate the natural text width first
lv_obj_set_width(msg_text, LV_SIZE_CONTENT);
lv_obj_update_layout(msg_text);
lv_coord_t text_width = lv_obj_get_width(msg_text);
// Ensure text width is not less than minimum width // Ensure text width is not less than minimum width
if (text_width < min_width) { if (text_width < min_width) {
text_width = min_width; text_width = min_width;
} }
// If text width is less than max width, use text width // Constrain to max width
if (text_width < max_width) { lv_coord_t bubble_width = (text_width < max_width) ? text_width : max_width;
bubble_width = text_width;
} else {
bubble_width = max_width;
}
// Set message text width // Set message text width
lv_obj_set_width(msg_text, bubble_width); // Subtract padding lv_obj_set_width(msg_text, bubble_width);
lv_label_set_long_mode(msg_text, LV_LABEL_LONG_WRAP); lv_label_set_long_mode(msg_text, LV_LABEL_LONG_WRAP);
// Set bubble width // Set bubble width
@ -1113,7 +1111,7 @@ void LcdDisplay::SetTheme(Theme* theme) {
if (lv_obj_get_child_cnt(obj) > 0) { if (lv_obj_get_child_cnt(obj) > 0) {
// Might be a container, check if it's a user or system message container // Might be a container, check if it's a user or system message container
// User and system message containers are transparent // User and system message containers are transparent
lv_opa_t bg_opa = lv_obj_get_style_bg_opa(obj, 0); lv_opa_t bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_MAIN);
if (bg_opa == LV_OPA_TRANSP) { if (bg_opa == LV_OPA_TRANSP) {
// This is a user or system message container // This is a user or system message container
bubble = lv_obj_get_child(obj, 0); bubble = lv_obj_get_child(obj, 0);

View File

@ -30,8 +30,8 @@ typedef struct Table {
static gd_GIF * gif_open(gd_GIF * gif); static gd_GIF * gif_open(gd_GIF * gif);
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file); static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file);
static void f_gif_read(gd_GIF * gif, void * buf, size_t len); static inline void f_gif_read(gd_GIF * gif, void * buf, size_t len);
static int f_gif_seek(gd_GIF * gif, size_t pos, int k); static inline int f_gif_seek(gd_GIF * gif, size_t pos, int k);
static void f_gif_close(gd_GIF * gif); static void f_gif_close(gd_GIF * gif);
#if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_HELIUM

View File

@ -20,21 +20,21 @@ dependencies:
espressif/esp_lcd_panel_io_additions: ^1.0.1 espressif/esp_lcd_panel_io_additions: ^1.0.1
78/esp_lcd_nv3023: ~1.0.0 78/esp_lcd_nv3023: ~1.0.0
78/esp-wifi-connect: ~3.0.2 78/esp-wifi-connect: ~3.0.2
espressif/esp_audio_effects: ~1.2.0 espressif/esp_audio_effects: ~1.2.1
espressif/esp_audio_codec: ~2.4.0 espressif/esp_audio_codec: ~2.4.1
78/esp-ml307: ~3.5.3 78/esp-ml307: ~3.5.3
78/uart-eth-modem: 78/uart-eth-modem:
version: ~0.1.3 version: ~0.2.1
rules: rules:
- if: target not in [esp32] - if: target not in [esp32]
78/xiaozhi-fonts: ~1.5.5 78/xiaozhi-fonts: ~1.5.5
espressif/led_strip: ~3.0.1 espressif/led_strip: ~3.0.2
espressif/esp_codec_dev: ~1.5 espressif/esp_codec_dev: ~1.5.4
espressif/esp-sr: ~2.2.0 espressif/esp-sr: ~2.3.0
espressif/button: ~4.1.3 espressif/button: ~4.1.5
espressif/knob: ^1.0.0 espressif/knob: ^1.0.0
espressif/esp32-camera: espressif/esp32-camera:
version: ^2.0.15 version: ^2.1.4
rules: rules:
- if: target in [esp32s3] - if: target in [esp32s3]
espressif/esp_video: espressif/esp_video:
@ -51,15 +51,15 @@ dependencies:
espressif/esp_lcd_touch_gt1151: ^1 espressif/esp_lcd_touch_gt1151: ^1
waveshare/esp_lcd_touch_cst9217: ^1.0.3 waveshare/esp_lcd_touch_cst9217: ^1.0.3
espressif/esp_lcd_touch_cst816s: ^1.0.6 espressif/esp_lcd_touch_cst816s: ^1.0.6
lvgl/lvgl: ~9.3.0 lvgl/lvgl: ~9.4.0
esp_lvgl_port: ~2.6.0 esp_lvgl_port: ~2.7.0
espressif/esp_io_expander_tca95xx_16bit: ^2.0.0 espressif/esp_io_expander_tca95xx_16bit: ^2.0.0
espressif2022/image_player: ^1.1.1 espressif2022/image_player: ^1.1.1
espressif2022/esp_emote_expression: ^0.1.0 espressif2022/esp_emote_expression: ^0.1.0
espressif/adc_mic: ^0.2.1 espressif/adc_mic: ^0.2.1
espressif/esp_mmap_assets: '>=1.2' espressif/esp_mmap_assets: '>=1.2'
txp666/otto-emoji-gif-component: txp666/otto-emoji-gif-component:
version: ^1.0.5 version: ^1.1.1
rules: rules:
- if: target in [esp32s3] - if: target in [esp32s3]
espressif/adc_battery_estimation: ^0.2.0 espressif/adc_battery_estimation: ^0.2.0

View File

@ -3,6 +3,8 @@
#include "settings.h" #include "settings.h"
#include "assets/lang_config.h" #include "assets/lang_config.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <cJSON.h> #include <cJSON.h>
#include <esp_log.h> #include <esp_log.h>
#include <esp_partition.h> #include <esp_partition.h>
@ -10,6 +12,7 @@
#include <esp_app_format.h> #include <esp_app_format.h>
#include <esp_efuse.h> #include <esp_efuse.h>
#include <esp_efuse_table.h> #include <esp_efuse_table.h>
#include <esp_heap_caps.h>
#ifdef SOC_HMAC_SUPPORTED #ifdef SOC_HMAC_SUPPORTED
#include <esp_hmac.h> #include <esp_hmac.h>
#endif #endif
@ -292,19 +295,28 @@ bool Ota::Upgrade(const std::string& firmware_url, std::function<void(int progre
return false; return false;
} }
char buffer[512]; constexpr size_t PAGE_SIZE = 4096;
char* buffer = (char*)heap_caps_malloc(PAGE_SIZE, MALLOC_CAP_INTERNAL);
if (buffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate buffer");
return false;
}
size_t buffer_offset = 0; // Current data size in buffer
size_t total_read = 0, recent_read = 0; size_t total_read = 0, recent_read = 0;
auto last_calc_time = esp_timer_get_time(); auto last_calc_time = esp_timer_get_time();
while (true) { while (true) {
int ret = http->Read(buffer, sizeof(buffer)); int ret = http->Read(buffer + buffer_offset, PAGE_SIZE - buffer_offset);
if (ret < 0) { if (ret < 0) {
ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret)); ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret));
heap_caps_free(buffer);
return false; return false;
} }
// Calculate speed and progress every second // Calculate speed and progress every second
recent_read += ret; recent_read += ret;
total_read += ret; total_read += ret;
buffer_offset += ret;
if (esp_timer_get_time() - last_calc_time >= 1000000 || ret == 0) { if (esp_timer_get_time() - last_calc_time >= 1000000 || ret == 0) {
size_t progress = total_read * 100 / content_length; size_t progress = total_read * 100 / content_length;
ESP_LOGI(TAG, "Progress: %u%% (%u/%u), Speed: %uB/s", progress, total_read, content_length, recent_read); ESP_LOGI(TAG, "Progress: %u%% (%u/%u), Speed: %uB/s", progress, total_read, content_length, recent_read);
@ -315,22 +327,16 @@ bool Ota::Upgrade(const std::string& firmware_url, std::function<void(int progre
recent_read = 0; recent_read = 0;
} }
if (ret == 0) {
break;
}
if (!image_header_checked) { if (!image_header_checked) {
image_header.append(buffer, ret); image_header.append(buffer, buffer_offset);
if (image_header.size() >= sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { if (image_header.size() >= sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
esp_app_desc_t new_app_info; esp_app_desc_t new_app_info;
memcpy(&new_app_info, image_header.data() + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t), sizeof(esp_app_desc_t)); memcpy(&new_app_info, image_header.data() + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t), sizeof(esp_app_desc_t));
auto current_version = esp_app_get_description()->version;
ESP_LOGI(TAG, "Current version: %s, New version: %s", current_version, new_app_info.version);
if (esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle)) { if (esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle)) {
esp_ota_abort(update_handle); esp_ota_abort(update_handle);
ESP_LOGE(TAG, "Failed to begin OTA"); ESP_LOGE(TAG, "Failed to begin OTA");
heap_caps_free(buffer);
return false; return false;
} }
@ -338,14 +344,27 @@ bool Ota::Upgrade(const std::string& firmware_url, std::function<void(int progre
std::string().swap(image_header); std::string().swap(image_header);
} }
} }
auto err = esp_ota_write(update_handle, buffer, ret);
// Write to flash when buffer is full (4KB) or it's the last chunk
bool is_last_chunk = (ret == 0);
if (buffer_offset == PAGE_SIZE || (is_last_chunk && buffer_offset > 0)) {
auto err = esp_ota_write(update_handle, buffer, buffer_offset);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to write OTA data: %s", esp_err_to_name(err));
esp_ota_abort(update_handle); esp_ota_abort(update_handle);
heap_caps_free(buffer);
return false; return false;
} }
buffer_offset = 0;
}
if (is_last_chunk) {
break;
}
} }
http->Close(); http->Close();
heap_caps_free(buffer);
esp_err_t err = esp_ota_end(update_handle); esp_err_t err = esp_ota_end(update_handle);
if (err != ESP_OK) { if (err != ESP_OK) {

View File

@ -119,7 +119,8 @@ bool MqttProtocol::StartMqttClient(bool report_error) {
auto alive = alive_; // Capture alive flag auto alive = alive_; // Capture alive flag
Application::GetInstance().Schedule([this, alive]() { Application::GetInstance().Schedule([this, alive]() {
if (*alive) { if (*alive) {
CloseAudioChannel(); // Server initiated goodbye, don't send goodbye back to avoid ping-pong
CloseAudioChannel(false);
} }
}); });
} }
@ -188,17 +189,23 @@ bool MqttProtocol::SendAudio(std::unique_ptr<AudioStreamPacket> packet) {
return udp_->Send(encrypted) > 0; return udp_->Send(encrypted) > 0;
} }
void MqttProtocol::CloseAudioChannel() { void MqttProtocol::CloseAudioChannel(bool send_goodbye) {
{ {
std::lock_guard<std::mutex> lock(channel_mutex_); std::lock_guard<std::mutex> lock(channel_mutex_);
udp_.reset(); udp_.reset();
} }
ESP_LOGI(TAG, "Closing audio channel, send_goodbye: %d", send_goodbye);
// Only send goodbye when client initiates the close
// Don't send if server already sent goodbye (to avoid ping-pong)
if (send_goodbye) {
std::string message = "{"; std::string message = "{";
message += "\"session_id\":\"" + session_id_ + "\","; message += "\"session_id\":\"" + session_id_ + "\",";
message += "\"type\":\"goodbye\""; message += "\"type\":\"goodbye\"";
message += "}"; message += "}";
SendText(message); SendText(message);
}
if (on_audio_channel_closed_ != nullptr) { if (on_audio_channel_closed_ != nullptr) {
on_audio_channel_closed_(); on_audio_channel_closed_();

View File

@ -31,7 +31,7 @@ public:
bool Start() override; bool Start() override;
bool SendAudio(std::unique_ptr<AudioStreamPacket> packet) override; bool SendAudio(std::unique_ptr<AudioStreamPacket> packet) override;
bool OpenAudioChannel() override; bool OpenAudioChannel() override;
void CloseAudioChannel() override; void CloseAudioChannel(bool send_goodbye = true) override;
bool IsAudioChannelOpened() const override; bool IsAudioChannelOpened() const override;
private: private:

View File

@ -65,7 +65,7 @@ public:
virtual bool Start() = 0; virtual bool Start() = 0;
virtual bool OpenAudioChannel() = 0; virtual bool OpenAudioChannel() = 0;
virtual void CloseAudioChannel() = 0; virtual void CloseAudioChannel(bool send_goodbye = true) = 0;
virtual bool IsAudioChannelOpened() const = 0; virtual bool IsAudioChannelOpened() const = 0;
virtual bool SendAudio(std::unique_ptr<AudioStreamPacket> packet) = 0; virtual bool SendAudio(std::unique_ptr<AudioStreamPacket> packet) = 0;
virtual void SendWakeWordDetected(const std::string& wake_word); virtual void SendWakeWordDetected(const std::string& wake_word);

View File

@ -75,7 +75,8 @@ bool WebsocketProtocol::IsAudioChannelOpened() const {
return websocket_ != nullptr && websocket_->IsConnected() && !error_occurred_ && !IsTimeout(); return websocket_ != nullptr && websocket_->IsConnected() && !error_occurred_ && !IsTimeout();
} }
void WebsocketProtocol::CloseAudioChannel() { void WebsocketProtocol::CloseAudioChannel(bool send_goodbye) {
(void)send_goodbye; // Websocket doesn't need to send goodbye message
websocket_.reset(); websocket_.reset();
} }

View File

@ -18,7 +18,7 @@ public:
bool Start() override; bool Start() override;
bool SendAudio(std::unique_ptr<AudioStreamPacket> packet) override; bool SendAudio(std::unique_ptr<AudioStreamPacket> packet) override;
bool OpenAudioChannel() override; bool OpenAudioChannel() override;
void CloseAudioChannel() override; void CloseAudioChannel(bool send_goodbye = true) override;
bool IsAudioChannelOpened() const override; bool IsAudioChannelOpened() const override;
private: private:

View File

@ -7,8 +7,8 @@ CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_SPIRAM=y CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=512 CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=2048
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=65536 CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=98304
CONFIG_SPIRAM_MEMTEST=n CONFIG_SPIRAM_MEMTEST=n
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y