CircuitPython 7 adds the
qrio module, which can decode QR codes from images grabbed with compatible cameras like the OV2640. (What is a QR code?)
These demos are designed to work with the Espressif Kaluga development kit, which comes with a compatible camera, can connect via WiFi to Adafruit IO, and has plenty of RAM to store the images for analysis.
This guide includes three demos:
- Scan To REPL: Each scanned and decoded barcode is printed in the REPL, so you can see it in mu or other compatible software
- Scan To USB HID: Each scanned and decoded barcode is typed into the connected host computer using a virtual keyboard
- Scan To Adafruit IO: Each scanned and decoded barcode is sent to Adafruit IO, where you can view it on a dashboard or connect it to triggers.
Additionally, each demo uses the Kaluga's LCD screen to show the live image in greyscale as well as the last scanned code.
For the Scan To Web demo, we've made sure the code works with the free version of Adafruit IO, so give it a try even if you haven't subscribed to IO+ yet.
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.
Create a QR Decoder object
You'll want to create the QR Decoder object just once in your program, if possible. When creating it, pass in the width and height of the image that you will later decode.
qrdecoder = qrio.QRDecoder(cam.width, cam.height)
Capture a greyscale or YUV image
qrio uses only the luminance, greyscale, or "Y" value of pixels (determined by the PixelPolicy value passed to its decode function). For the OV2640 camera, placing it in YUV mode and then using the
EVEN_BYTES policy gets the correct data.
cam.colorspace = adafruit_ov2640.OV2640_COLOR_YUV
bitmap = displayio.Bitmap(cam.width, cam.height, 65536)
Retrieve decoded strings
A QR code is usually ASCII, but the result of decoding with
qrio is a byte string "payload" and an encoding name. If it is ASCII or UTF-8, you can decode it with the decode method; if that fails, you can turn it into the ASCII representation of the byte string, which will print like
for row in qrdecoder.decode(bitmap, qrio.PixelPolicy.EVEN_BYTES):
payload = row.payload
payload = payload.decode("utf-8")
payload = str(payload)
To keep the program more responsive, the image is captured at a limited 160x120 resolution. This puts a limit on how complex the QR code can be, because each dot within the QR code needs to be at least a couple of pixels big in the recorded image.
Many OV2640 cameras don't focus clearly on close objects, and have trouble taking properly exposed pictures of LCD and OLED screens. QR codes 75 to 100mm (3" to 4" inches) across printed on card stock seem to work best at a scanning distance from 150mm to 300mm (6" to 12"), while scanning from a phone screen is frustrating and rarely works.
A "fish-eye" or wide-angle lens will distort the QR code in a way that qrio doesn't correct for. You need a normal or telephoto lens to scan QR codes. If straight lines don't look straight in your camera, it's not going to work.