Skip to content

Commit 08a3d05

Browse files
drlinuxcursoragent
andcommitted
fix(esp32): gate RGB LCD driver to ESP32-S3 and add port helpers
Skip rgb_lcd_display_driver on non-S3 targets so AtomVM esp32 CI builds pass without esp_lcd_panel_rgb.h. Add boot framebuffer fill, measure_text port command, and document measure_text in display-drivers.md. Signed-off-by: Ibrahim YILMAZ <ibrahim@drlinux.org> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9818a83 commit 08a3d05

5 files changed

Lines changed: 90 additions & 5 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
# WHOLE_ARCHIVE option is supported only with esp-idf 5.x
2222
# A link option will be used with esp-idf 4.x
23-
if (IDF_VERSION_MAJOR GREATER_EQUAL 5)
23+
if (IDF_VERSION_MAJOR GREATER_EQUAL 5 AND IDF_TARGET STREQUAL "esp32s3")
2424
set(OPTIONAL_WHOLE_ARCHIVE WHOLE_ARCHIVE)
2525
set(OPTIONAL_ESP_LCD_REQUIRES "esp_lcd")
2626
set(OPTIONAL_RGB_LCD_SRCS "rgb_lcd_display_driver.c")

display_driver.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#include <stdlib.h>
2222

23+
#include <sdkconfig.h>
24+
2325
#include <esp_idf_version.h>
2426
#include <esp_log.h>
2527

@@ -34,7 +36,7 @@ Context *epaper_display_create_port(GlobalContext *global, term opts);
3436
Context *dcs_lcd_display_create_port(GlobalContext *global, term opts);
3537
Context *memory_lcd_display_create_port(GlobalContext *global, term opts);
3638
Context *oled_display_create_port(GlobalContext *global, term opts);
37-
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
39+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && CONFIG_IDF_TARGET_ESP32S3
3840
Context *rgb_lcd_display_create_port(GlobalContext *global, term opts);
3941
#endif
4042

@@ -58,7 +60,7 @@ Context *display_create_port(GlobalContext *global, term opts)
5860
if (!strcmp(compat_string, "waveshare,5in65-acep-7c")
5961
|| !strcmp(compat_string, "good-display/gdep073e01")) {
6062
ctx = epaper_display_create_port(global, opts);
61-
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
63+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && CONFIG_IDF_TARGET_ESP32S3
6264
} else if (!strcmp(compat_string, "waveshare,esp32-s3-touch-lcd-7")
6365
|| !strcmp(compat_string, "esp_lcd,rgb")) {
6466
ctx = rgb_lcd_display_create_port(global, opts);

display_task.c

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,72 @@ static bool try_handle_register_font(Message *message, Context *ctx)
144144
return true;
145145
}
146146

147+
static bool try_handle_measure_text(Message *message, Context *ctx)
148+
{
149+
GenMessage gen_message;
150+
if (UNLIKELY(port_parse_gen_message(message->message,
151+
&gen_message) != GenCallMessage)) {
152+
return false;
153+
}
154+
155+
term req = gen_message.req;
156+
if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 3)) {
157+
return false;
158+
}
159+
term cmd = term_get_tuple_element(req, 0);
160+
161+
if (cmd != globalcontext_make_atom(ctx->global,
162+
"\xC" "measure_text")) {
163+
return false;
164+
}
165+
166+
char *handle = interop_atom_to_string(ctx,
167+
term_get_tuple_element(req, 1));
168+
EpdFont *loaded_font = NULL;
169+
if (handle != NULL) {
170+
loaded_font = ufont_manager_find_by_handle(ufont_manager, handle);
171+
free(handle);
172+
}
173+
174+
term text_bin = term_get_tuple_element(req, 2);
175+
size_t text_len = term_binary_size(text_bin);
176+
char *text = malloc(text_len + 1);
177+
if (text == NULL) {
178+
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap);
179+
term return_tuple = term_alloc_tuple(2, &heap);
180+
term_put_tuple_element(return_tuple, 0, gen_message.ref);
181+
term_put_tuple_element(return_tuple, 1, ERROR_ATOM);
182+
display_message_send(gen_message.pid, return_tuple, ctx->global);
183+
END_WITH_STACK_HEAP(heap, ctx->global);
184+
return true;
185+
}
186+
memcpy(text, term_binary_data(text_bin), text_len);
187+
text[text_len] = '\0';
188+
189+
int width = 0;
190+
int height = 0;
191+
if (loaded_font != NULL) {
192+
EpdFontProperties props = epd_font_properties_default();
193+
EpdRect rect = epd_get_string_rect(loaded_font, text, 0, 0, 0, &props);
194+
width = rect.width;
195+
height = rect.height;
196+
}
197+
free(text);
198+
199+
BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(3) + TUPLE_SIZE(2) + REF_SIZE, heap);
200+
term result = term_alloc_tuple(3, &heap);
201+
term_put_tuple_element(result, 0, OK_ATOM);
202+
term_put_tuple_element(result, 1, term_from_int(width));
203+
term_put_tuple_element(result, 2, term_from_int(height));
204+
term return_tuple = term_alloc_tuple(2, &heap);
205+
term_put_tuple_element(return_tuple, 0, gen_message.ref);
206+
term_put_tuple_element(return_tuple, 1, result);
207+
display_message_send(gen_message.pid, return_tuple, ctx->global);
208+
END_WITH_STACK_HEAP(heap, ctx->global);
209+
210+
return true;
211+
}
212+
147213
void display_task_process_messages(void *arg)
148214
{
149215
struct DisplayTaskArgs *args = arg;
@@ -154,7 +220,8 @@ void display_task_process_messages(void *arg)
154220
Message *message;
155221
xQueueReceive(args->messages_queue, &message, portMAX_DELAY);
156222

157-
if (!try_handle_register_font(message, args->ctx)) {
223+
if (!try_handle_register_font(message, args->ctx)
224+
&& !try_handle_measure_text(message, args->ctx)) {
158225
args->process_message_fn(message, args->ctx);
159226
}
160227

docs/display-drivers.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,16 @@ like progress bars or dynamic text fields where a full-screen redraw is unnecess
395395
:port.call(display, {:update_region, x, y, width, height, display_list}, 500)
396396
```
397397

398+
### measure_text
399+
400+
Returns the pixel width and height of a text string for a registered uFont handle.
401+
Use this to size marquee regions or layout before building a display list.
402+
403+
```elixir
404+
# {:ok, width, height} or {:error, reason}
405+
:port.call(display, {:measure_text, :default16px, "Hello"}, 500)
406+
```
407+
398408
### draw_buffer
399409

400410
Draws a preformatted RGB565 buffer already resident in memory. Each pixel is 2 bytes

rgb_lcd_display_driver.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949

5050
static const char *TAG = "rgb_lcd_display_driver";
5151

52+
/* Matches display_server COLOR_BG (16#1A1A); solid fill before first scanout. */
53+
#define RGB565_BOOT_FILL_BYTE 0x1A
54+
5255
struct RGBLCDDriver
5356
{
5457
esp_lcd_panel_handle_t panel;
@@ -1037,10 +1040,13 @@ static void display_init(Context *ctx, term opts)
10371040
void *fb1 = NULL;
10381041
err = esp_lcd_rgb_panel_get_frame_buffer(driver->panel, 2, &fb0, &fb1);
10391042
if (err == ESP_OK && fb0 && fb1) {
1043+
size_t fb_bytes = (size_t) width * (size_t) height * sizeof(uint16_t);
1044+
memset(fb0, RGB565_BOOT_FILL_BYTE, fb_bytes);
1045+
memset(fb1, RGB565_BOOT_FILL_BYTE, fb_bytes);
10401046
driver->framebuffers[0] = fb0;
10411047
driver->framebuffers[1] = fb1;
10421048
driver->framebuffer_count = 2;
1043-
ESP_LOGI(TAG, "Using RGB LCD double framebuffer: %p %p", fb0, fb1);
1049+
ESP_LOGI(TAG, "Using RGB LCD double framebuffer: %p %p (boot fill)", fb0, fb1);
10441050
} else {
10451051
ESP_LOGW(TAG, "RGB LCD multi-framebuffer unavailable, using draw_bitmap path: %s", esp_err_to_name(err));
10461052
}

0 commit comments

Comments
 (0)