QR Code Scanner
This program is designed for the ESP32-S3-EYE development kit.
qrio
, the built-in module for decoding QR codes, is discussed in more detail in its dedicated guide.
Whenever it "sees" a QR code, it will print the contained data on the REPL. It also shows the live view on the LCD.
Your project will use a specific set of CircuitPython libraries and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Drag the contents of the uncompressed bundle directory onto your Feather board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
Once the demo starts, it will look for a QR code and print it on the REPL. While it works, it'll show the camera image on the viewfinder so you can line up the code within the frame.
This code works great with a standard or telephoto lens, but a wide-angle or fish-eye lens distorts the QR code and prevents CircuitPython from decoding it.
Scanning from a computer LCD or phone screen works sometimes, but scanning from paper is more reliable. It helps if the image is large (several inches or 75 to 100mm), since most cameras don't have good close focus for a small (two inches or 50mm or smaller) codes.

# SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries # # SPDX-License-Identifier: Unlicense """ This demo is designed for the Kaluga development kit version 1.3 with the ILI9341 display. """ import struct import board import espcamera import qrio print("Initializing camera") cam = espcamera.Camera( data_pins=board.CAMERA_DATA, external_clock_pin=board.CAMERA_XCLK, pixel_clock_pin=board.CAMERA_PCLK, vsync_pin=board.CAMERA_VSYNC, href_pin=board.CAMERA_HREF, pixel_format=espcamera.PixelFormat.RGB565, frame_size=espcamera.FrameSize.R240X240, i2c=board.I2C(), external_clock_frequency=20_000_000, framebuffer_count=2) cam.vflip = True cam.hmirror = True board.DISPLAY.auto_refresh = False display_bus = board.DISPLAY.bus print(cam.width, cam.height) qrdecoder = qrio.QRDecoder(cam.width, cam.height) print(qrdecoder.width, qrdecoder.height) #raise SystemExit ow = (board.DISPLAY.width - cam.width) // 2 oh = (board.DISPLAY.height - cam.height) // 2 display_bus.send(42, struct.pack(">hh", ow, cam.width + ow - 1)) display_bus.send(43, struct.pack(">hh", oh, cam.height + ow - 1)) while True: frame = cam.take(1) display_bus.send(44, frame) for row in qrdecoder.decode(frame, qrio.PixelPolicy.RGB565_SWAPPED): payload = row.payload try: payload = payload.decode("utf-8") except UnicodeError: payload = str(payload) print(payload) print(end=".")
The QR decoder is specific to the size of image, so it needs to be created to match the camera's image dimensions:
qrdecoder = qrio.QRDecoder(cam.width, cam.height)
Each frame is simply passed on to the QR decoder, which returns zero or more pieces of data. Because the data might or might not be valid UTF-8, we try two ways of decoding it into a printable value.
# (inside the forever-loop) for row in qrdecoder.decode(frame, qrio.PixelPolicy.RGB565_SWAPPED): payload = row.payload try: payload = payload.decode("utf-8") except UnicodeError: payload = str(payload) print(payload)
Creating QR Codes
There are lots of sites & services to generate QR codes. This one seems pretty no-nonsense.
If you have Python installed on your host computer, you can use the Adafruit miniQR Library to show QR codes in a terminal window.
If you use the Duck Duck Go search engine, you can search for "qr" + your terms, like so. It can help to use the web browser's magnification function (ctrl-+ or command-+) to adjust the image to be bigger on screen.

Page last edited January 22, 2025
Text editor powered by tinymce.