Table of Contents
Introduction
It is nice to improve the usability of electronic products. There are lots of valuable technologies for this, such as Near-Field Communication (NFC), and QR codes, that could help produce solutions with better user experience.
QR codes in the context of microcontrollers could be useful for directing a user towards a user manual or web portal. They could also be handy for displaying error codes, which users could scan with their mobile phone to obtain detailed troubleshooting tips or even click to call an engineer around!
I decided to spend an evening trying to implement QR codes on a Pi Pico, using a small display!
The end result was a solution that doesn’t require a lot of resources; perhaps around 250 bytes of ROM is sufficient to store a typical QR code (the example in the photo above for instance, takes up 156 bytes), it will depend on the amount of information being embedded.
This short blog post covers two topics:
(1) How to generate a QR code on a PC, and save it as a small C array of data
(2) How to use a microcontroller to interpret that data and present it as a QR code on a display
The code can be used with any microcontroller and any LCD screen, provided a single LCD function is available to draw a rectangle in black or white!
First Steps
Although any board will work, if you wish to try it using the same display I’m using, then the part code is Waveshare Pico LCD 1.3 (it is a board that can plug onto a Pi Pico, and it contains a tiny 240x240 LCD screen and ST7789 SPI-controlled driver chip, and some buttons for creating user interfaces). It works great as a quick testbed.
Incidentally, the LCD was stuck on with a slight incline, which bugged me, so I used a guitar pick to prise up the display, removed the sticky tape, and then used a small sheet of plastic for thickness, and applied double-sided tape on both sides of it, to make a flat surface for the display at the end away from the flat-flex hinge.
Next, I went to the manufacturer's website to download the LCD driver code for the Pi Pico, and it helpfully provided primitives for basic shapes such as filled rectangles. I was ready to start coding!
Strategy
There’s two schools of thought: either pre-generate QR codes, or generate them on-the-fly. Both have their uses; an on-the-fly QR code could be convenient for transmitting dynamic data, but will use more program space and more RAM.
I didn’t have a need for dynamic QR codes, so I decided to save microcontroller resources by pre-computing the QR code data on a PC before incorporating it into the firmware.
Generating QR Codes on a PC
This was relatively easy; there’s a Python library that can generate QR codes, albeit in a PNG format, which I didn’t actually want.
I created some code in a file called qr_gen.py that calls the library function to create a QR code PNG temporary file, and then the code reads and converts the PNG file contents into a compact C array of bytes, where I simply encoded every eight pixels from the QR code into a single byte, except at the end of each line, which may have padding bits if the length of a line isn’t a multiple of 8.
To generate a QR code, all that needs to be done is to type a single command line:
python qr_gen.py "this is the text for the QR code"
If you wanted to store a web link inside the QR code, you could type:
python qr_gen.py https://bbc.co.uk/news
Once you type that, the following output appears:
That entire output can be copy-pasted into a C file for your microcontroller of choice.
Converting QR Code Data into LCD Output
A single function was created that can be copy-pasted into any microcontroller project. It could be tidied a little bit and made more efficient, but I didn’t see a need since it is code that only needs to run at the speed that a human is happy seeing it rendered to a screen! It draws near-instantaneously using a Pi Pico and a typical LCD driver library.
The code requires the following parameters:
LCD_WIDTH
and LCD_HEIGHT
are the pixel dimensions of the screen, required so that the QR code can be sized automatically and centered on the screen.
The following LCD functions are needed:
LCD_CLEAR(WHITE)
is used to set the entire display to white.
LCD_RECT(x1, y1, x2, y2, BLACK)
is used to paint a black square with corner co-ordinates (x1,y1) and (x2,y2).
LCD_SEND_BUF()
is optional, it is a placeholder if your LCD driver code uses such a function to update the displayed image.
Summary
QR codes can be useful for making electronic products easier to use or troubleshoot, especially if the target user is not an engineer. It turns out that it is very low-hanging fruit to add QR code display capability on a microcontroller, especially if the QR code is pre-calculated. The generated C code is very small and requires hardly any resources to render to a display in real time.
The code is on GitHub. It would be great to hear about other QR code-related (or any other usability-related) technologies that people have used or are planning to use in their projects.
Thanks for reading!