An Arduino QR code display using a matrix of 8×8 LED dot matrix displays driven by the MAX7219 display driver.
8×8 LED dot matrix displays AliExpress
The displays are chainable up to 8 displays in one chain. Multiple chains need to be used to drive more displays.
Arduino has 14 digital pins and 6 analog pins which can be used as digital. It can support up to 6 chains (3 SPI pins per chain: DIN, CLK and CS). That results in the maximum display size of 48×48 dots (6×6 displays to keep the QR square). This allows displaying QR codes of versions 1-7 or displaying a version 1 code at twice the size.
Version | Pixels | Displays | Dots | Utilization |
---|---|---|---|---|
QR 1 | 21×21 (441) | 9 (3×3) | 24×24 (576) | 77 % (441/576) |
QR 1 @2x | 42×42 (1764) | 36 (6×6) | 48×48 (2304) | 76 % (1764/2304) |
QR 2 | 25×25 (625) | 16 (4×4) | 32×32 (1024) | 61 % (625/1024) |
QR 3 | 29×29 (841) | 16 (4×4) | 32×32 (1024) | 82 % (841/1024) |
QR 4 | 33×33 (1089) | 25 (5×5) | 40×40 (1600) | 68 % (1089/1600) |
QR 5 | 37×37 (1369) | 25 (5×5) | 40×40 (1600) | 85 % (1369/1600) |
QR 6 | 41×41 (1681) | 36 (6×6) | 48×48 (2304) | 72 % (1681/2304) |
QR 7 | 45×45 (2025) | 36 (6×6) | 48×48 (2304) | 87 % (2025/2304) |
QR | NH | NQ | NM | NL | ANH | ANQ | ANM | ANL |
---|---|---|---|---|---|---|---|---|
1 | 17 | 27 | 34 | 41 | 10 | 16 | 20 | 25 |
2 | 34 | 48 | 63 | 77 | 20 | 29 | 38 | 47 |
3 | 58 | 77 | 101 | 127 | 35 | 47 | 61 | 77 |
4 | 82 | 111 | 149 | 187 | 50 | 67 | 90 | 114 |
5 | 106 | 144 | 202 | 255 | 64 | 87 | 122 | 154 |
6 | 139 | 178 | 255 | 322 | 84 | 108 | 154 | 195 |
7 | 154 | 207 | 293 | 370 | 93 | 125 | 178 | 224 |
QR | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
NH | 17 | 34 | 58 | 82 | 106 | 139 | 154 |
NQ | 27 | 48 | 77 | 111 | 144 | 178 | 207 |
NM | 34 | 63 | 101 | 149 | 202 | 255 | 293 |
NL | 41 | 77 | 127 | 187 | 255 | 322 | 370 |
ANH | 10 | 20 | 35 | 50 | 64 | 84 | 93 |
ANQ | 16 | 29 | 47 | 67 | 87 | 108 | 125 |
ANM | 20 | 38 | 61 | 90 | 122 | 154 | 178 |
ANL | 25 | 47 | 77 | 114 | 154 | 195 | 224 |
N = numeric, AN = alpha-numeric, H = high, Q = quartile, M = medium, L = low
Information Capacity (QRCode.com)
See qr.ino
for the WIP live Arduino code. Compared to the simulation
below, it adds Bluetooth communication support and uses correct wiring to how
the LED dot matrix displays I got behave, they seem to differ a bit from the
simulated ones.
See wokwi.ino
and diagram.json
for the Wokwi
simulation files. Compared to the live Arduino code above, there is no Bluetooth
communication support.
I build a 3×3 display grid and wired it up to an Arduino Uno. The wiring was a bit different from the Wokwi simulation.
- Include the real hardware wiring diagram
- Add photos+videos of the QR code in action including scanning screencast
- Document the HC-06 and HM-10 (MLT-BT05) difference (not BLE versus BLE)
- iOS seems to only support BLE (maybe BT 4.0) based Bluetooth serial modules
- Android supports only non-BLE with my Samsung A41 test phone and the app Arduino bluetooth controller
- Document https://www.arduino.cc/en/Reference/SoftwareSerial serial bridge
- Echo Bluetooth serial to serial data to debug in Arduino IDE Serial Monitor
- Echo serial to Bluetooth serial data to debug in BLExAR iOS app Console
- Document pins
Danila Loginov built a WebBluetooth terminal capable of talking to the HM-10/MLT-BT05 BLE module. It works for me in Chrome on macOS and using Bluefy on iOS.
It doesn't work with the HC-06 Bluetooth 2.0/3.0 module. It seems WebBluetooth only supports Bluetooth 4.0/BLE since macOS itself did see both modules.
I ordered a bunch of white dot matrix displays to see if they scan faster/more reliably.
The Pico can mount itself as a mass storage device, so the text file QR content source idea would be that much easier.
Test this on actual hardware. Check other libraries too, to find whichever is able to control the most displays.
According to Stack Overflow and the Wokwi simulation above, analog pins can be used as digital pins. If on real hardware this works, it bumps us from a 4×4 display to a 6×6 display.
EZ Expander (NootropicDesign.com)
I've ordered 3 and they should arrive in a week or two.
14 digital pins - 3 used shield pins + 13 new shield pins + 6 analog pins = 30
30 pins ~ 10 chains.
If the chains are truly stuck at 8 displays at most, this would give us a 8×8 display, 64×64 dots, QR level up to 11 (61×61). Or QR version 3 at double size. Version 11 numeric capacity is 331-772 and alphanumeric 200-468.
If the chains could somehow be 10 displays long, that would give us a 10×10 display, 100×100 dots, QR level up to 20 (97×97). Or QR version 7, double sized. Version 20 numeric capacity is 919-2061 and alphanumeric 557-1249.
If the chains really need to be at most 8 displays, we could still achieve a 9×9 display by using 8 chains for an 8×8 display and then two chains for the 9th row and column. This would complicate the code calculations, but could be worth it. 9×9 is 72×72 dots, QR level up to version 13 (69×69). Or QR version 4, doubled. Version 13 numeric capacity is 427-1022 and alphanumeric 259-619.
There are some improvement / new feature ideas in there.
Instead of attempting to squeeze the rat's nest of wires into the 3d printed box or to use solder bridge connections between the displays to avoid the excessive amount of wires, let's order a PCB where the driver boards could be mounted such that the display grid works out perfectly aligned and the Arduino could be held on the backside of the PCB, or even on the front side too in sort of a base.
Consider whether I want to have an on-board battery and a switch or if I'll just run a USB cable out from the enclosure to connect into a powerbank.
The load of this device is basically constant, so it should be doable to find an approximate duration I can expect this to last… and then verify it in the real world.
At the very least, leading and trailing space could be removed. Perhaps length and fit into the QR code storage for the given level could be checked, too. The indication of an incorrect value could be the display grid going blank or blinking.
To confirm the payload was set and how it was adjusted (trim, case).
I found this QR generator online which allows controlling the level, too:
https://www.nayuki.io/page/qr-code-generator-library
My QR codes scan, but they appear different from the ones generated by this lib. I should find out why that is to understand if I have any problems in my QR code setup.
This is an undocumented part in Wokwi. It should be fun to hook it up to a single display and see the driving signals for things like lighting up the edge LEDs and clearing/filling the display. Maybe it will also reveal the difference between pushing in a single row/column as a single number/call in LEDControl and setting the individual LEDs. The output format of the logic analyzer is VCD, openable by PulseView (Open > Import Value Change Dump data).
The complexity of the code would increase if I were to use either of these methods, but if the performance improves, it might be justifiable. Although the current "rolling" animation is pretty pleasant to look at, so maybe fast refresh is not needed?
The thing starts up, presents a QR code, scanned by the Authenticator app on the phone, this OTP QR code can generate 6 digit codes which are then used to prefix the message in the Bluetooth control app such that messages which do not have this prefix are rejected and the QT code shown is not changed. Use HOTP not TOTP to avoid replay attack within the TOTP window (or keep a tracking variable of whether the current window TOTP code was already used once).