When the first personal computers were shipped, thick printed manuals were included to enable people to use them properly. Later, there were help files available which filled a CD completely. But what can you do when you deliver your project based on a microcontroller? Well, you might fix a sticker on it leading to your homepage. In case your project contains a graphic display, you can show a QR code containing the URL of the instructions like the one below:
The same picture reduced to 1 pixel per square:
Unfortunately, it takes some steps to produce these links. Two different methods will be shown how it can be done.
Method ZeroWritten ten month after (not ten years after).
The aim was to shorten the toolchain and make it safer to proceed. Using different graphics software it was found that resizing often produced artefacts, so one should use the qr code produced originally by the generator and do as litttle as possible to it. I still assume we have only a TFT width of 128 pixels. This led to
- Step 1: enter yout text. Make it as short as possible. If it is a URL you can drop the "https://".
You can play with the frames width in order to match the size of 150x150.
Save it in the lossless PNG format selecting a width of 150. - Step 2: Open the file and remove the borders to make it fit in 128x128, better even less but do not cut the black modules and save it in BMP format with a color depth of 1 (monochrome).
- Step 3: Open the file using a Hex-editor like HxD and export the data as a ".C"-File, but rename it to ".h". Move that file to the directory where your Arduino sketch resides in.
- Step 4: Use the following code to show the qr-code on your TFT display.
#include <TFT.h>
#define cs 10
#define dc 9
#define rst 8
Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, rst);
const PROGMEM
#include "qrcode2.h"
void setup() {
tft.initR(INITR_BLACKTAB);
tft.fillScreen(0);
const uint8_t* adr = &rawData[0x3E];
unsigned char wQR = pgm_read_byte(&rawData[0x12]);
unsigned char hQR = pgm_read_byte(&rawData[0x16]);
tft.drawBitmap(0, 16, adr, wQR, hQR, 0xFFFF);
}
void loop() {}
The dark side of this way is: it is using some 2000 bytes for the bitmap instead of just 200 used in the other methods but it saves a lot of time and possible errors.
Method 1: vectorsThere is quite a number of steps to be executed one after the other.
- Find an online QR generator like https://goqr.me and enter the URL you are going to point to.
- Press DOWNLOAD and select EPS. You will get a file called qrcode.eps.
This is part of the EPS file you will get:
0 0 moveto
1 0 lineto
1 1 lineto
0 1 lineto
0 0 lineto
1 0 moveto
2 0 lineto
2 1 lineto
1 1 lineto
1 0 lineto
Now
use the DOS program FIND to remove all the "lineto"-lines:
C:\> FIND "moveto" qrcode.eps > moveto.txt
- Next, use your favorite text editor (notepad will do) to edit the "moveto.txt" file and delete the headlines.
- Then replace all the "moveto" by nothing; only the moveto, not the figures preceding it.
- Then replace all blanks " " by commas.
- Save this file as moveto.h.
Having done this it will look like the one shown below. The file now contains only the lower left corners of all the black squares. Note that in EPS the y-axis goes from bottom to top opposite to most other graphical systems. It will start like this:
0,0,
1,0,
2,0,
3,0,
4,0,
5,0,
6,0,
8,0,
9,0,
10,0,
12,0,
13,0,
drawing the first line of the big orientation point in the lower left corner. As you can see, there is no "7, 0, " this location remains white in all valid QR codes.
Now you can #include this file in your main file in order to show the QR code that leads to the location you want to give. Do not forget to edit the FILENAME line.
// print QR code on 1.8" TFT display
#include <TFT.h>
#define cs 10
#define dc 9
#define rst 8
TFT tft = TFT(cs, dc, rst);
#define FILENAME "www.hackster.io_qr.h"
PROGMEM const byte qr[] = {
#include FILENAME
};
void setup() {
Serial.begin(115200);
Serial.println(__FILE__);
int s = sizeof qr;
byte maxi = 0;
for (int i = 0; i < s; i++)
maxi = max(maxi, pgm_read_byte(qr + i));
tft.begin();
tft.setRotation(0);
byte w = tft.width();
byte h = tft.height();
byte r = w / maxi;
Serial.println(s);
Serial.println(maxi);
Serial.println(r);
byte x0 = (w - maxi * r) / 2 - 2;
byte y0 = (h - (maxi - 1) * r) / 2;
tft.background(ST7735_WHITE);
tft.setTextColor(0);
tft.setCursor(1, 1);
tft.println(FILENAME);
tft.setCursor(1, 9);
tft.print("r:");
tft.print(r);
tft.print(" width:");
tft.print(maxi + 1);
tft.print(" size:");
tft.print(sizeof qr);
for (int i = 0; i < s; i++) {
byte x = x0 + r * pgm_read_byte(qr + i++);
byte y = h - y0 - r * pgm_read_byte(qr + i);
tft.fillRect(x, y, r, r, 0);
}
}
void loop() {}
By
using PROGMEM all data will be stored in FLASH and not fill all your precious SRAM space.
This program will print the QR code in portrait mode.
Method 2: pixelsThere is quite a number of steps to be performed:
- As above, use an online QR generator, but this time DOWNLOAD the graphic as PNG (lossless compression) in maximum resolution (1000x1000).
- Find the number of modules by checking the grid. It should be 21x21, or 25x25, or 29x29,....
- When you are going to download the code make sure you select "0" for the size of the border.
- After downloading, count how many elements the QR code takes in either axis. You will need this number for the next step.
- Use a simple graphics program such as paint.exe to reduce the size from 1000x1000 to your actual grid (do not try to find the per cent value) and save it as monochrom bitmap file (bitmap.bmp). Do not be surprised when you get a rather small picture.
- Use a Hex editor like HxD.exe (download: https://mh-nexus.de/en/) to open this file.
The contents might look like this:
The value at address 0x0A points to the location 0x3E where the image pixels start. The values at 0x12 and 0x16 give width and height of the image, in this example 0x19 = 25(dec).
- In HxD use File --> export --> C. You will get a file called "bitmap.c". You better rename it to "bitmap.h". It might look like this:
unsigned char rawData[162] = {
0x42, 0x4D, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x19, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00,
0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0x00, 0x01, 0x23, 0xE0, 0x00, 0x7D, 0x0C, 0x23, 0x00, 0x45, 0x58,
0x79, 0x00, 0x45, 0x32, 0xD0, 0x00, 0x45, 0x00, 0x05, 0x80, 0x7D, 0x8D,
0x73, 0x00, 0x01, 0x07, 0x54, 0x00, 0xFF, 0x7A, 0x73, 0x80, 0x5C, 0x10,
0x05, 0x80, 0x47, 0x16, 0xA7, 0x00, 0x45, 0x18, 0x02, 0x00, 0x36, 0xD3,
0x2A, 0x80, 0xED, 0xAA, 0x84, 0x00, 0xA2, 0x0F, 0xAF, 0x00, 0x25, 0xA2,
0x02, 0x00, 0xCA, 0x3D, 0x2E, 0x80, 0x04, 0x11, 0xAA, 0x80, 0xFF, 0xF5,
0xFF, 0x80, 0x01, 0x55, 0x40, 0x00, 0x7D, 0x5C, 0x5F, 0x00, 0x45, 0xD7,
0x51, 0x00, 0x45, 0x28, 0x51, 0x00, 0x45, 0x8F, 0xD1, 0x00, 0x7D, 0x24,
0x5F, 0x00, 0x01, 0xBD, 0x40, 0x00
};
The
size of data is much smaller compared to method 1. In the unlikely case you run out of memory you can add the PROGMEM as shown above. Now use this sketch in your Arduino UNO:
// read bitmap and print QR code on 1.8" TFT display
#include <TFT.h>
#define cs 10
#define dc 9
#define rst 8
TFT tft = TFT(cs, dc, rst);
#include "bitmap.h"
void setup() {
Serial.begin(115200);
Serial.println(__FILE__);
byte start = rawData[0x0A];
byte max = rawData[0x12] - 1;
Serial.println(sizeof rawData);
Serial.println(start);
Serial.println(max);
tft.begin();
tft.setRotation(0);
tft.fillScreen(ST7735_WHITE);
tft.setTextColor(0);
tft.print("QR code");
byte w = tft.width();
byte h = tft.height();
byte r = w / max;
byte lm = (w - max * r) / 2 - 1; // left margin
for (byte i = start, x = lm, y = (h + max * r) / 2, k = 0; i < sizeof rawData; i++) {
byte m = rawData[i];
for (int j = 0; j < 8; j++, k++, m = m * 2, x = x + r) {
if ((m & 0x80) == 0) tft.fillRect(x, y, r, r, 0);
if (k >= max) {
// start a new line and leave current loop:
k = 0;
x = lm;
y = y - r;
break;
}
}
}
}
void loop() {}
Note
that we used multiple comma-separated declarations and assignments in the for-loops.
A QR code like this should be displayed on your TFT:
In practical applications you would move the code in setup to a function to be called when some key is pressed.
Comments