99#include " esp_timer.h"
1010#include " esp_heap_caps.h"
1111#include " esp_websocket_client.h"
12+ #include " esp_efuse.h"
1213#include " freertos/FreeRTOS.h"
1314#include " freertos/task.h"
1415
1516namespace esphome {
1617namespace remote_webview {
1718
18- static const char *const TAG = " Remove_WebView " ;
19+ static const char *const TAG = " Remote_WebView " ;
1920RemoteWebView *RemoteWebView::self_ = nullptr ;
2021
2122void RemoteWebView::setup () {
@@ -26,7 +27,10 @@ void RemoteWebView::setup() {
2627 return ;
2728 }
2829
29- q_decode_ = xQueueCreate (cfg::decode_queue_depth, sizeof (WsMsg));
30+ display_width_ = display_->get_width ();
31+ display_height_ = display_->get_height ();
32+
33+ q_decode_ = xQueueCreate (cfg::decode_queue_depth, sizeof (WsMsg));
3034 ws_send_mtx_ = xSemaphoreCreateMutex ();
3135
3236 start_decode_task_ ();
@@ -35,8 +39,45 @@ void RemoteWebView::setup() {
3539 if (touch_) {
3640 touch_listener_ = new RemoteWebViewTouchListener (this );
3741 touch_->register_listener (touch_listener_);
38- ESP_LOGI (TAG, " touch listener registered" );
42+ ESP_LOGD (TAG, " touch listener registered" );
43+ }
44+
45+ #if REMOTE_WEBVIEW_HW_JPEG
46+ jpeg_decode_engine_cfg_t jcfg = {
47+ .timeout_ms = 200 ,
48+ };
49+ if (jpeg_new_decoder_engine (&jcfg, &hw_dec_) != ESP_OK) {
50+ hw_dec_ = nullptr ;
51+ }
52+
53+ if (hw_dec_) {
54+ const int W = display_->get_width ();
55+ const int H = display_->get_height ();
56+ const int aligned_w = (W + 15 ) & ~15 ;
57+ const int aligned_h = (H + 15 ) & ~15 ;
58+
59+ const size_t max_buffer_size = (size_t )aligned_w * (size_t )aligned_h * 2u ;
60+
61+ jpeg_decode_memory_alloc_cfg_t in_cfg { .buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER };
62+ jpeg_decode_memory_alloc_cfg_t out_cfg { .buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER };
63+
64+ hw_decode_input_buf_ = (uint8_t *)jpeg_alloc_decoder_mem ((uint32_t )max_buffer_size, &in_cfg, &hw_decode_input_size_);
65+ hw_decode_output_buf_ = (uint8_t *)jpeg_alloc_decoder_mem ((uint32_t )max_buffer_size, &out_cfg, &hw_decode_output_size_);
66+
67+ if (!hw_decode_input_buf_ || !hw_decode_output_buf_) {
68+ ESP_LOGE (TAG, " Failed to allocate HW decoder buffers" );
69+ if (hw_decode_input_buf_) free (hw_decode_input_buf_);
70+ if (hw_decode_output_buf_) free (hw_decode_output_buf_);
71+ hw_decode_input_buf_ = nullptr ;
72+ hw_decode_output_buf_ = nullptr ;
73+ jpeg_del_decoder_engine (hw_dec_);
74+ hw_dec_ = nullptr ;
75+ } else {
76+ ESP_LOGD (TAG, " HW decoder buffers allocated: input=%u, output=%u" ,
77+ (unsigned )hw_decode_input_size_, (unsigned )hw_decode_output_size_);
78+ }
3979 }
80+ #endif
4081}
4182
4283void RemoteWebView::dump_config () {
@@ -49,6 +90,12 @@ void RemoteWebView::dump_config() {
4990 ESP_LOGCONFIG (TAG, " display: %dx%d" , display_->get_width (), display_->get_height ());
5091 }
5192
93+ #if REMOTE_WEBVIEW_HW_JPEG
94+ ESP_LOGCONFIG (TAG, " hw_jpeg: %s" , hw_dec_ ? " yes" : " no" );
95+ #else
96+ ESP_LOGCONFIG (TAG, " hw_jpeg: no" );
97+ #endif
98+
5299 ESP_LOGCONFIG (TAG, " server: %s:%d" , server_host_.c_str (), server_port_);
53100 ESP_LOGCONFIG (TAG, " url: %s" , url_.c_str ());
54101
@@ -79,7 +126,7 @@ bool RemoteWebView::open_url(const std::string &s) {
79126
80127 if (ws_send_open_url_ (s.c_str (), 0 )) {
81128 url_ = s;
82- ESP_LOGI (TAG, " opened URL: %s" , s.c_str ());
129+ ESP_LOGD (TAG, " opened URL: %s" , s.c_str ());
83130 return true ;
84131 }
85132
@@ -145,7 +192,7 @@ void RemoteWebView::ws_event_handler_(void *handler_arg, esp_event_base_t, int32
145192
146193 case WEBSOCKET_EVENT_DISCONNECTED:
147194 if (self_) self_->ws_client_ = nullptr ;
148- ESP_LOGW (TAG, " [ws] disconnected" );
195+ ESP_LOGI (TAG, " [ws] disconnected" );
149196 if (self_) self_->last_keepalive_us_ = 0 ;
150197 reasm_reset_ (*r);
151198 break ;
@@ -160,8 +207,11 @@ void RemoteWebView::ws_event_handler_(void *handler_arg, esp_event_base_t, int32
160207
161208 if (e->payload_offset == 0 ) {
162209 reasm_reset_ (*r);
163- if ((size_t )e->payload_len > cfg::ws_max_message_bytes) {
164- ESP_LOGE (TAG, " WS message too large: %u > %u" , (unsigned )e->payload_len , (unsigned )cfg::ws_max_message_bytes);
210+ const size_t max_allowed = (self_ && self_->max_bytes_per_msg_ > 0 )
211+ ? (size_t )self_->max_bytes_per_msg_
212+ : cfg::ws_max_message_bytes;
213+ if ((size_t )e->payload_len > max_allowed) {
214+ ESP_LOGE (TAG, " WS message too large: %u > %u" , (unsigned )e->payload_len , (unsigned )max_allowed);
165215 break ;
166216 }
167217 r->total = (size_t )e->payload_len ;
@@ -252,15 +302,12 @@ void RemoteWebView::process_frame_packet_(const uint8_t *data, size_t len)
252302 frame_bytes_ += len;
253303 frame_tiles_ += fi.tile_count ;
254304
255- const int FB_W = display_ ? display_->get_width () : 480 ;
256- const int FB_H = display_ ? display_->get_height () : 480 ;
257-
258305 for (uint16_t i = 0 ; i < fi.tile_count ; i++) {
259306 proto::TileHeader th{};
260307 if (!proto::parse_tile_header (data, len, th, off)) return ;
261308 if (off + th.dlen > len) return ;
262309
263- if (th.w == 0 || th.h == 0 || th.w > FB_W || th.h > FB_H ) {
310+ if (th.w == 0 || th.h == 0 || th.w > display_width_ || th.h > display_height_ ) {
264311 off += th.dlen ;
265312 continue ;
266313 }
@@ -270,7 +317,6 @@ void RemoteWebView::process_frame_packet_(const uint8_t *data, size_t len)
270317 }
271318
272319 off += th.dlen ;
273- taskYIELD ();
274320 }
275321
276322 if (fi.flags & proto::kFlafLastOfFrame ) {
@@ -305,19 +351,68 @@ void RemoteWebView::process_frame_stats_packet_(const uint8_t *data, size_t len)
305351}
306352
307353bool RemoteWebView::decode_jpeg_tile_to_lcd_ (int16_t dst_x, int16_t dst_y, const uint8_t *data, size_t len) {
308- if (!display_ || ! data || len == 0 ) return false ;
354+ if (!data || ! len) return false ;
309355
356+ #if REMOTE_WEBVIEW_HW_JPEG
357+ if (hw_dec_ && hw_decode_input_buf_ && hw_decode_output_buf_) {
358+ jpeg_decode_picture_info_t hdr{};
359+ if (jpeg_decoder_get_info (data, (uint32_t )len, &hdr) != ESP_OK || !hdr.width || !hdr.height ) {
360+ return decode_jpeg_tile_software_ (dst_x, dst_y, data, len);
361+ }
362+
363+ const int aligned_w = (hdr.width + 15 ) & ~15 ;
364+ const int aligned_h = (hdr.height + 15 ) & ~15 ;
365+ const uint32_t out_sz = (uint32_t )aligned_w * (uint32_t )aligned_h * 2u ;
366+
367+ if (aligned_w != (int )hdr.width ) {
368+ ESP_LOGW (TAG, " jpeg dimensions not aligned: %u x %u" , (unsigned )hdr.width , (unsigned )hdr.height );
369+ return decode_jpeg_tile_software_ (dst_x, dst_y, data, len);
370+ }
371+
372+ if (len > hw_decode_input_size_ || out_sz > hw_decode_output_size_) {
373+ ESP_LOGW (TAG, " tile too large for HW decoder buffers" );
374+ return decode_jpeg_tile_software_ (dst_x, dst_y, data, len);
375+ }
376+
377+ jpeg_decode_cfg_t jcfg{};
378+ jcfg.output_format = JPEG_DECODE_OUT_FORMAT_RGB565;
379+ jcfg.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR;
380+ jcfg.conv_std = JPEG_YUV_RGB_CONV_STD_BT709;
381+
382+ memcpy (hw_decode_input_buf_, data, len);
383+
384+ uint32_t written = 0 ;
385+ esp_err_t dr = jpeg_decoder_process (hw_dec_, &jcfg, hw_decode_input_buf_, (uint32_t )len,
386+ hw_decode_output_buf_, (uint32_t )hw_decode_output_size_, &written);
387+
388+ if (dr != ESP_OK) {
389+ return decode_jpeg_tile_software_ (dst_x, dst_y, data, len);
390+ }
391+
392+ display_->draw_pixels_at (dst_x, dst_y, (int )hdr.width , (int )hdr.height , hw_decode_output_buf_,
393+ esphome::display::COLOR_ORDER_RGB,
394+ esphome::display::COLOR_BITNESS_565,
395+ rgb565_big_endian_);
396+
397+ return true ;
398+ }
399+ #endif // REMOTE_WEBVIEW_HW_JPEG
400+
401+ return decode_jpeg_tile_software_ (dst_x, dst_y, data, len);
402+ }
403+
404+ bool RemoteWebView::decode_jpeg_tile_software_ (int16_t dst_x, int16_t dst_y, const uint8_t *data, size_t len) {
310405 if (!jd_.openRAM ((uint8_t *)data, (int )len, &RemoteWebView::jpeg_draw_cb_s_)) {
311- ESP_LOGW (TAG, " openRAM failed (len=%u) err=%d" , (unsigned )len, jd_.getLastError ());
406+ ESP_LOGE (TAG, " openRAM failed (len=%u) err=%d" , (unsigned )len, jd_.getLastError ());
312407 return false ;
313408 }
314409
315- jd_.setMaxOutputSize (4 * 2048 );
410+ jd_.setMaxOutputSize (8 * 2048 );
316411 jd_.setPixelType (rgb565_big_endian_ ? RGB565_BIG_ENDIAN : RGB565_LITTLE_ENDIAN);
317412
318413 const int rc = jd_.decode (dst_x, dst_y, 0 );
319414 if (rc == 0 ) {
320- ESP_LOGW (TAG, " decode rc=%d err=%d" , rc, jd_.getLastError ());
415+ ESP_LOGE (TAG, " decode rc=%d err=%d" , rc, jd_.getLastError ());
321416 jd_.close ();
322417 return false ;
323418 }
@@ -330,24 +425,19 @@ int RemoteWebView::jpeg_draw_cb_s_(JPEGDRAW *p) {
330425}
331426
332427int RemoteWebView::jpeg_draw_cb_ (JPEGDRAW *p) {
333- if (!display_) return 0 ;
334-
335428 int32_t x = p->x , y = p->y , w = p->iWidth , h = p->iHeight ;
336- const int FB_W = display_->get_width ();
337- const int FB_H = display_->get_height ();
338-
339- if (x >= FB_W || y >= FB_H) return 1 ;
340- if (x + w > FB_W) w = FB_W - x;
341- if (y + h > FB_H) h = FB_H - y;
429+
430+ if (x >= display_width_ || y >= display_height_) return 1 ;
431+ if (x + w > display_width_) w = display_width_ - x;
432+ if (y + h > display_height_) h = display_height_ - y;
342433 if (w <= 0 || h <= 0 ) return 1 ;
343434
344- const bool big_endian = rgb565_big_endian_;
345435 display_->draw_pixels_at (
346436 x, y, w, h,
347437 (const uint8_t *)p->pPixels ,
348438 esphome::display::COLOR_ORDER_RGB,
349439 esphome::display::COLOR_BITNESS_565,
350- big_endian
440+ rgb565_big_endian_
351441 );
352442
353443 return 1 ;
@@ -357,7 +447,6 @@ bool RemoteWebView::ws_send_touch_event_(proto::TouchType type, int x, int y, ui
357447 if (!ws_client_ || !ws_send_mtx_ || !esp_websocket_client_is_connected (ws_client_))
358448 return false ;
359449
360- // clamp into 16-bit
361450 if (x < 0 ) x = 0 ; if (y < 0 ) y = 0 ;
362451 if (x > 65535 ) x = 65535 ; if (y > 65535 ) y = 65535 ;
363452
@@ -384,10 +473,10 @@ bool RemoteWebView::ws_send_open_url_(const char *url, uint16_t flags) {
384473
385474 const uint32_t n = (uint32_t ) strlen (url);
386475 const size_t total = sizeof (proto::OpenURLHeader) + (size_t ) n;
387- if (total > cfg::ws_max_message_bytes) return false ;
476+
477+ if (total > 16 * 1024 ) return false ;
388478
389- // try PSRAM first
390- auto *pkt = (uint8_t *) heap_caps_malloc (total, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
479+ auto *pkt = (uint8_t *) heap_caps_malloc (total, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
391480 if (!pkt) pkt = (uint8_t *) heap_caps_malloc (total, MALLOC_CAP_8BIT);
392481 if (!pkt) return false ;
393482
@@ -495,21 +584,31 @@ std::string RemoteWebView::resolve_device_id_() const {
495584 if (!device_id_.empty ()) return device_id_;
496585
497586 uint8_t mac[6 ] = {0 };
587+ esp_err_t err = ESP_FAIL;
588+
498589#if ESP_IDF_VERSION_MAJOR >= 5
499- if (esp_read_mac (mac, ESP_MAC_WIFI_STA) != ESP_OK) {
500- (void ) esp_read_mac (mac, ESP_MAC_BT); // best-effort fallback
590+ err = esp_read_mac (mac, ESP_MAC_WIFI_STA);
591+ if (err != ESP_OK) {
592+ err = esp_read_mac (mac, ESP_MAC_BT);
593+ }
594+ if (err != ESP_OK) {
595+ err = esp_read_mac (mac, ESP_MAC_ETH);
596+ }
597+ if (err != ESP_OK) {
598+ err = esp_efuse_mac_get_default (mac);
501599 }
502600#else
503- // Older IDF: try to get base MAC from eFuse
504- // (some SDKs declare esp_efuse_mac_get_default in esp_system.h)
505- extern " C" esp_err_t esp_efuse_mac_get_default (uint8_t *mac);
506- if (esp_efuse_mac_get_default) {
507- (void ) esp_efuse_mac_get_default (mac);
508- } else {
509- (void ) esp_read_mac (mac, ESP_MAC_WIFI_STA);
601+ err = esp_efuse_mac_get_default (mac);
602+ if (err != ESP_OK) {
603+ err = esp_read_mac (mac, ESP_MAC_WIFI_STA);
510604 }
511605#endif
512606
607+ if (err != ESP_OK) {
608+ ESP_LOGW (TAG, " Failed to read MAC address, using random ID" );
609+ snprintf ((char *)mac, sizeof (mac), " %06lx" , (unsigned long )esp_random ());
610+ }
611+
513612 char buf[32 ];
514613 snprintf (buf, sizeof (buf), " esp32-%02x%02x%02x%02x%02x%02x" ,
515614 mac[0 ], mac[1 ], mac[2 ], mac[3 ], mac[4 ], mac[5 ]);
@@ -524,10 +623,8 @@ std::string RemoteWebView::build_ws_uri_() const {
524623 const std::string id = resolve_device_id_ ();
525624 append_q_str_ (uri, " id" , id.c_str ());
526625
527- const int W = display_ ? display_->get_width () : 0 ;
528- const int H = display_ ? display_->get_height () : 0 ;
529- append_q_int_ (uri, " w" , W);
530- append_q_int_ (uri, " h" , H);
626+ append_q_int_ (uri, " w" , display_width_);
627+ append_q_int_ (uri, " h" , display_height_);
531628
532629 append_q_int_ (uri, " r" , rotation_);
533630 append_q_int_ (uri, " ts" , tile_size_);
0 commit comments