QR codes have become a ubiquitous way to encode and share information, URLs, contact details, configuration parameters, and more. With LVGL, embedding a QR code into your embedded UI is straightforward. In this post, we’ll explore how to use QR codes in LVGL, discuss the limits of QR code sizing on embedded displays, and take a step back to reflect on the design philosophy behind these clever square patterns.
Using QR Codes in LVGL
Starting from LVGL v8, the library includes a built-in widget for generating and displaying QR codes.
Here's a simple example:
lv_obj_t * qr = lv_qrcode_create(lv_scr_act(), 100, lv_color_black(), lv_color_white());
lv_qrcode_update(qr, "https://www.edgemtech.com");
This snippet creates a 100x100 pixel QR code in black on a white background. The lv_qrcode_update function encodes the string into the QR pattern.
Display Limitations and Scaling
The size of your QR code is constrained by:
The screen resolution
The desired readability (especially when using a camera)
The error correction level and data complexity
LVGL supports scaling the QR pattern with pixel-perfect precision. However, embedded displays often have limited resolution. For example, on a 128x128 screen, you might only fit a version 2 QR code (~25x25 modules) cleanly, assuming each module is 4-5 pixels.
⚠️ Rule of thumb: Always scale each module (square) to at least 3x3 pixels for reliable scanning.
A Word on QR Code Structure
QR codes are built using a defined standard (ISO/IEC 18004), consisting of:
Finder patterns: the large corner squares
Alignment patterns: help correct distortion
Timing patterns: guide scanning across rows/columns
Error correction: allows reading even if damaged
The LVGL QR widget internally uses a compact QR generation algorithm, based on qrcodegen, optimized for embedded use. You won’t need to implement these patterns yourself, but understanding them helps debug issues (e.g., too much data, unreadable codes).
Philosophy: More Than a Matrix
What makes QR codes elegant is their balance between redundancy and compression. They:
Handle data loss gracefully with Reed-Solomon error correction
Encode multiple formats (text, binary, etc.)
Remain visually consistent while encoding vastly different content
In an embedded system, this philosophy matches our priorities: reliability, compactness, and clarity. With LVGL, you can embed this visual language into your UI effortlessly.
LVGL QR Code Integration Limitations
While the QR code widget in LVGL provides a convenient interface for rendering QR codes, it’s important to be aware of its design constraints and lack of configurability, especially if you need finer control for production-level applications.

🔚Conclusion
LVGL’s QR code integration is simple, efficient, and well-suited for many embedded use cases, especially where memory and performance matter.
It gets the job done when you need to display a basic QR code, and it integrates cleanly into the LVGL object model using lv_canvas. However, it stops short of offering full control. Features like error correction level, mask pattern selection, quiet zone size, and detailed error feedback are fixed or not exposed. For most developers, this may not be an issue, but for those needing fine-tuned QR generation (e.g., for branding, versioning, or resilience), the current implementation feels limiting.
That said… who knows? Maybe a patch is on the way to bring these missing features to LVGL. Stay tuned 😉