[2021] basic use of C language qrencode QR Code Generation Library under Linux and transplantation of ARM development board

Posted by rea|and on Thu, 30 Dec 2021 15:02:43 +0100

1, Foreword

Because the working product equipment needs a function of dynamically generating QR code according to the IP address of the equipment. After scanning the QR code, the user can access the web service running in the device, and then configure the relevant information with the mobile phone.

Because our embedded devices use C language, we need to find a more suitable library for QR code generation of C language if we don't want to build wheels from scratch.

There are many libraries on the Internet. Finally, qrencode written by a Japanese God was determined. The library is libqrencode.

qrencode official website: https://fukuchi.org/works/qrencode/

qrencode open source github address: https://github.com/fukuchi/libqrencode

You can see that there are still a lot of people who pay attention to qrencode.

Also from the Internet search, many people have a good evaluation of it. So we decided to use qrencode as our QR code generation library.

There are still very few materials to use qrencode under Linux, and there will always be some strange problems.

When solving problems, most of them are used in combination with QT under windows. So I spent a lot of time solving the problem of using Linux. This article will describe it in detail.

I wonder why I have to use QT. Almost all articles from Baidu are the same. I just want to write a different one.

2, Prepare the environment and version information used

1. Ubuntu and kernel versions

I also did experiments on the Ubuntu 20 version, no problem

zh@zh:~$ uname -a
Linux zh 4.18.0-15-generic #16~18.04.1-Ubuntu SMP Thu Feb 7 14:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
zh@zh:~$

2. gcc and g + + versions

I have done experiments on Ubuntu 20. gcc and g + + are both 9 X no problem at all.

zh@zh:~$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$
zh@zh:~$
zh@zh:~$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$

3. Cross compile gcc and g + + versions

I won't introduce how to install it. You should be an old hand when you see this article.
Because my own development board is based on ARM architecture, I installed the cross compilation tool of arm architecture.

zh@zh:~$ arm
arm2hpdl                        arm-linux-gnueabihf-gcc-6.2.1   arm-linux-gnueabihf-ld.gold
arm-linux-gnueabihf-addr2line   arm-linux-gnueabihf-gcc-ar      arm-linux-gnueabihf-nm
arm-linux-gnueabihf-ar          arm-linux-gnueabihf-gcc-nm      arm-linux-gnueabihf-objcopy
arm-linux-gnueabihf-as          arm-linux-gnueabihf-gcc-ranlib  arm-linux-gnueabihf-objdump
arm-linux-gnueabihf-c++         arm-linux-gnueabihf-gcov        arm-linux-gnueabihf-ranlib
arm-linux-gnueabihf-c++filt     arm-linux-gnueabihf-gcov-tool   arm-linux-gnueabihf-readelf
arm-linux-gnueabihf-cpp         arm-linux-gnueabihf-gdb         arm-linux-gnueabihf-size
arm-linux-gnueabihf-dwp         arm-linux-gnueabihf-gfortran    arm-linux-gnueabihf-strings
arm-linux-gnueabihf-elfedit     arm-linux-gnueabihf-gprof       arm-linux-gnueabihf-strip
arm-linux-gnueabihf-g++         arm-linux-gnueabihf-ld
arm-linux-gnueabihf-gcc         arm-linux-gnueabihf-ld.bfd
zh@zh:~$
zh@zh:~$
zh@zh:~$ arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (Linaro GCC 6.2-2016.11) 6.2.1 20161016
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$
zh@zh:~$ arm-linux-gnueabihf-g++ --version
arm-linux-gnueabihf-g++ (Linaro GCC 6.2-2016.11) 6.2.1 20161016
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

zh@zh:~$
zh@zh:~$

4. Development board information

I bought 100ask from Mr. Wei Dongshan's Taobao store some time ago_ Imx6ull Pro development board.

It looks like this: it is based on arm architecture.

The kernel version of the development board is as follows:

[root@100ask:~]# uname -a
Linux 100ask 4.9.88 #1 SMP PREEMPT Sat Jul 24 07:41:11 EDT 2021 armv7l GNU/Linux
[root@100ask:~]#

3, Compiling & installing qrencode in development environment

1. Download qrencode source code

Open: https://fukuchi.org/works/qrencode/

Now the latest is 4.1 1 version:

After downloading:

2. Upload to Linux

You can use the FileZilla tool and any tool that can be uploaded to Linux.

After uploading:

zh@zh:~$ ls -al qrencode-4.1.1.tar.gz
-rw-rw-r-- 1 zh zh 543245 Aug  7 10:36 qrencode-4.1.1.tar.gz
zh@zh:~$

Extract:

zh@zh:~$ tar zxvf qrencode-4.1.1.tar.gz

3. Compilation and installation

Because we are now compiling and installing in the development environment, we can ignore some configuration information and directly default.

to configure:

zh@zh:~/qrencode-4.1.1$ ./configure

compile:

zh@zh:~/qrencode-4.1.1$ make

Installation:

zh@zh:~/qrencode-4.1.1$ sudo make install

You can see some directories to install to:

----------------------------------------------------------------------
 /bin/mkdir -p '/usr/local/bin'
  /bin/bash ./libtool   --mode=install /usr/bin/install -c qrencode '/usr/local/bin'
libtool: install: /usr/bin/install -c .libs/qrencode /usr/local/bin/qrencode
 /bin/mkdir -p '/usr/local/include'
 /usr/bin/install -c -m 644 qrencode.h '/usr/local/include'
 /bin/mkdir -p '/usr/local/share/man/man1'
 /usr/bin/install -c -m 644 qrencode.1 '/usr/local/share/man/man1'
 /bin/mkdir -p '/usr/local/lib/pkgconfig'
 /usr/bin/install -c -m 644 libqrencode.pc '/usr/local/lib/pkgconfig'
make[2]: Leaving directory '/home/zh/qrencode-4.1.1'
make[1]: Leaving directory '/home/zh/qrencode-4.1.1'
zh@zh:~/qrencode-4.1.1$

After installation, you can see:

zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$ ls /usr/local/include/
qrencode.h
zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$ ls /usr/local/bin/
qrencode
zh@zh:~/qrencode-4.1.1$
zh@zh:~/qrencode-4.1.1$ ls /usr/local/lib/
libqrencode.la  libqrencode.so  libqrencode.so.4  libqrencode.so.4.1.1  pkgconfig  python2.7  python3.6
zh@zh:~/qrencode-4.1.1$

I have compiled and installed Ubuntu 18 (GCC v7. X) and Ubuntu 20 (GCC V9. X) without any problems. If you have any questions, you can also discuss them together.

4. Write qrencode test program

Here is a very complete test program that has been tested by me.
Core code: there is only one QRcode_encodeString method

//main.cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <qrencode.h>

using namespace std;

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;

//Bitmap file header definition;
typedef struct tagBITMAPFILEHEADER
{
    //WORD bfType;// If it is read separately, it is not defined in the structure
    DWORD bfSize;     //file size
    WORD bfReserved1; //Reserved word
    WORD bfReserved2; //Reserved word
    DWORD bfOffBits;  //The number of bytes offset from the file header to the actual bitmap data
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER
{
    DWORD biSize;          //Header size
    DWORD biWidth;         //Image width
    DWORD biHeight;        //Image height
    WORD biPlanes;         //Number of bit planes, must be 1
    WORD biBitCount;       //bits per pixel 
    DWORD biCompression;   //Compression type
    DWORD biSizeImage;     //Compressed image size bytes
    DWORD biXPelsPerMeter; //Horizontal resolution
    DWORD biYPelsPerMeter; //Vertical resolution
    DWORD biClrUsed;       //The number of colors actually used by the bitmap
    DWORD biClrImportant;  //The number of important colors in the standard map
} BITMAPINFOHEADER;        //Bitmap header definition

//Pixel information
typedef struct tagIMAGEDATA
{
    BYTE blue;
    BYTE green;
    BYTE red;
} DATA;




int main()
{
	//You can replace it with what you want to set
    //const char *QRTEXT = "haha,";

	//You can also place links to jump
    const char *QRTEXT = "https://blog.csdn.net/qq_17623363";
	
    QRcode *qrCode;
    int version = 5; //Set the version number, which is set to 5 here, and the corresponding size is 37 * 37
    QRecLevel level = QR_ECLEVEL_H;
    QRencodeMode hint = QR_MODE_8;
    int casesensitive = 1;

    qrCode = QRcode_encodeString(QRTEXT, version, level, hint, casesensitive);
    if (NULL == qrCode)
    {
        printf("QRcode create fail\n");
        return -1;
    }

    //Save the QR code to be generated as a BMP true color picture file
    FILE *pf = fopen("qrcode.bmp", "wb");
    if (NULL == pf)
    {
        printf("file open fail.\n");
        fclose(pf);
        return -1;
    }
    int width = qrCode->width;
    int height = qrCode->width;
    int biCount = 24;                                 //True color
    int lineByte = (width * biCount / 8 + 3) / 4 * 4; //Bytes per line must be a multiple of 4
    //bitmap-file header 
    BITMAPFILEHEADER bitMapFileHeader;
    //bitMapFileHeader.bfType = 0x4D42;
    bitMapFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lineByte * height;
    bitMapFileHeader.bfReserved1 = 0;
    bitMapFileHeader.bfReserved2 = 0;
    bitMapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    //Bitmap header
    BITMAPINFOHEADER bitMapInfoHeader;
    bitMapInfoHeader.biBitCount = biCount;
    bitMapInfoHeader.biClrImportant = 0;
    bitMapInfoHeader.biClrUsed = 0;
    bitMapInfoHeader.biCompression = 0;
    bitMapInfoHeader.biHeight = height;
    bitMapInfoHeader.biPlanes = 1;
    bitMapInfoHeader.biSize = 40;
    bitMapInfoHeader.biSizeImage = lineByte * (height);
    bitMapInfoHeader.biWidth = width;
    bitMapInfoHeader.biXPelsPerMeter = 0;
    bitMapInfoHeader.biYPelsPerMeter = 0;

    WORD bfType = 0x4D42;

    //BM type
    fwrite(&bfType, sizeof(WORD), 1, pf);
    //Write file header into file
    fwrite(&bitMapFileHeader, sizeof(BITMAPFILEHEADER), 1, pf);
    //Write bitmap header into file
    fwrite(&bitMapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pf);
    unsigned char *pBMPData = new unsigned char[lineByte * height];
    memset(pBMPData, 255, lineByte * height);

    unsigned char *qrData = qrCode->data;
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < lineByte / 3; j++)
        {
            if (*(qrData)&1)
            {
                //Set rgb color, which can be customized. It is set to black here.
                *(pBMPData + lineByte * i + 3 * j) = 0;
                *(pBMPData + lineByte * i + 3 * j + 1) = 0;
                *(pBMPData + lineByte * i + 3 * j + 2) = 0;
            }
            qrData++;
        }
    }

    //Write data into file
    fwrite(pBMPData, sizeof(unsigned char), lineByte * height, pf);
    fclose(pf);
    delete[] pBMPData;
    pBMPData = NULL;

    QRcode_free(qrCode);
    return 0;
}

5. Upload the c + + file to Ubuntu

After uploading:

zh@zh:~/qrcode$ ls
main.cpp
zh@zh:~/qrcode$

compile:

zh@zh:~/qrcode$ g++ main.cpp -o QRTest -I/usr/local/include -L/usr/local/lib -lqrencode
zh@zh:~/qrcode$
zh@zh:~/qrcode$ ll
-rw-rw-r--  1 zh zh  4254 Aug  8 03:22 main.cpp
-rwxrwxr-x  1 zh zh 13168 Aug  8 03:27 QRTest*
zh@zh:~/qrcode$

Execute executable program to generate QR Code:

zh@zh:~/qrcode$ ./QRTest
zh@zh:~/qrcode$
zh@zh:~/qrcode$ ll
-rw-rw-r--  1 zh zh  4254 Aug  8 03:22 main.cpp
-rw-rw-r--  1 zh zh  4198 Aug  8 03:27 qrcode.bmp
-rwxrwxr-x  1 zh zh 13168 Aug  8 03:27 QRTest*
zh@zh:~/qrcode$

qrcode.bmp is the QR code image generated by us. We can download this image to windows and view it; If you are the graphical interface of ubuntu, you can also view it directly.

The following is the QR code image we want:

You can scan and try.

In this way, the development environment is successfully configured!

4, ARM development board porting and testing qrencode

When compiling, we can choose to compile into static library or dynamic library.
The basic knowledge of static library and dynamic library is not discussed in this article, so you need to understand it yourself.

As for the version information of my cross compilation tool, I have already mentioned it, so I won't repeat it here.

1. Compile into dynamic library

#Create a directory to store the path of dynamic library installation, which is convenient for us to find and migrate
mkdir /home/zh/libqrencode-arm-so
 
#Unzip the source code
tar xvzf qrencode-4.1.1.tar.gz

# Enter the extracted qrencode-4.1 1 catalog
cd qrencode-4.1.1

# Configure the prefix path and the prefix of the cross compilation tool chain
./configure --prefix=/home/zh/libqrencode-arm-so --host= arm-linux-gnueabihf --without-tools
 
#compile
make
#install
make install

If you don't make mistakes, you will succeed.

If we transplant, we need the following things

zh@zh:~$ ls /home/zh/libqrencode-arm-so/
include  lib
zh@zh:~$
zh@zh:~$
zh@zh:~$ tree /home/zh/libqrencode-arm-so
/home/zh/libqrencode-arm-so
├── include
│   └── qrencode.h
└── lib
    ├── libqrencode.la
    ├── libqrencode.so -> libqrencode.so.4.1.1
    ├── libqrencode.so.4 -> libqrencode.so.4.1.1
    ├── libqrencode.so.4.1.1
    └── pkgconfig
        └── libqrencode.pc

3 directories, 6 files
zh@zh:~$
zh@zh:~$

2. Compile into static library

#Create a directory to store the path of dynamic library installation, which is convenient for us to find and migrate
mkdir /home/zh/libqrencode-arm-static 
 
#Unzip the source code
tar xvzf qrencode-4.1.1.tar.gz

# Enter the extracted qrencode-4.1 1 catalog
cd qrencode-4.1.1

# Configure the prefix path and the prefix of the cross compilation tool chain
./configure --prefix=/home/zh/libqrencode-arm-static --host=arm-linux-gnueabihf --without-tools --enable-static --disable-shared

#compile
make
#install
make install

After successful installation:

zh@zh:~$ ls /home/zh/libqrencode-arm-static/
include  lib
zh@zh:~$
zh@zh:~$ tree /home/zh/libqrencode-arm-static/
/home/zh/libqrencode-arm-static/
├── include
│   └── qrencode.h
└── lib
    ├── libqrencode.a
    ├── libqrencode.la
    └── pkgconfig
        └── libqrencode.pc

3 directories, 4 files
zh@zh:~$

3. Compiling and installing Linux x86

#Configuration, self configurable
./configure
#compile
make
#install
make install

4. Cross compile test program

For simplicity and understanding, the Makefile form is not used

zh@zh:~/qrcode$ arm-linux-gnueabihf-g++  -I/home/zh/libqrencode-arm-so/include   -c -o main.o main.cpp
zh@zh:~/qrcode$
zh@zh:~/qrcode$
zh@zh:~/qrcode$ arm-linux-gnueabihf-g++ -o Qrcode -g main.o -I/home/zh/libqrencode-arm-so/include  -L/home/zh/libqrencode-arm-so/lib -lqrencode

After cross compilation, we cannot execute normally on Ubuntu development environment:

zh@zh:~/qrcode$ ls
main.cpp   Qrcode  qrcode.bmp  QRTest
zh@zh:~/qrcode$

#Cross compiled is not executable
zh@zh:~/qrcode$ ./Qrcode
-bash: ./Qrcode: cannot execute binary file: Exec format error
zh@zh:~/qrcode$

#The original can be executed
zh@zh:~/qrcode$ ./QRTest 
zh@zh:~/qrcode$

You can view the version information:

You can see that QRTest is on x86-64 platform;
Qrcode is the of ARM platform.

zh@zh:~/qrcode$ file QRTest
QRTest: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=65e6c48fca725e09171b5cb79924671810e5e7ad, not stripped
zh@zh:~/qrcode$
zh@zh:~/qrcode$
zh@zh:~/qrcode$ file Qrcode
Qrcode: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=942712d2411951cb747b0dcfed3302993acc8de8, with debug_info, not stripped
zh@zh:~/qrcode$
zh@zh:~/qrcode$

4. Taking dynamic library as an example

(1) Copy all files of test code and dynamic library to the development board

I mount an nfs, which is convenient for debugging.

[root@100ask:/mnt]# df
Filesystem                        1K-blocks     Used Available Use% Mounted on
/dev/root                           1497056   605844    809008  43% /
devtmpfs                              87344        0     87344   0% /dev
tmpfs                                251696        0    251696   0% /dev/shm
tmpfs                                251696      304    251392   1% /tmp
tmpfs                                251696      512    251184   1% /run
192.168.1.200:/home/zh/nfs_rootfs 277709056 23534592 239997952   9% /mnt
[root@100ask:/mnt]#
[root@100ask:/mnt]#
[root@100ask:/mnt]# pwd
/mnt
[root@100ask:/mnt]#

Copy the necessary files to the directory of the nfs share:

zh@zh:~$ cp -arf /home/zh/libqrencode-arm-so /home/zh/nfs_rootfs/
zh@zh:~$
zh@zh:~$ cp -arf /home/zh/qrcode /home/zh/nfs_rootfs/
zh@zh:~$

Then we can see on the development board:

[root@100ask:/mnt/qrcode]# ls
QRTest  Qrcode  main.cpp  qrcode.bmp
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ls /mnt/libqrencode-arm-so/
include  lib

In order to better test whether it is successful, we delete the original QR code image

[root@100ask:/mnt/qrcode]# rm qrcode.bmp
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ls
Makefile  Makefile2  QRTest  Qrcode  main.cpp
[root@100ask:/mnt/qrcode]#

Test successful:

It can be seen that it cannot succeed, and libqrencode is missing so. 4 documents

[root@100ask:/mnt/qrcode]# ./Qrcode
./Qrcode: error while loading shared libraries: libqrencode.so.4: cannot open shared object file: No such file or directory
[root@100ask:/mnt/qrcode]#

We copy the cross compiled dynamic library file to the / usr/lib directory on the arm development board:

[root@100ask:/mnt/qrcode]# cp -p /mnt/libqrencode-arm-so/
include/ lib/
[root@100ask:/mnt/qrcode]# cp -p /mnt/libqrencode-arm-so/lib/
libqrencode.la        libqrencode.so.4      pkgconfig/
libqrencode.so        libqrencode.so.4.1.1
[root@100ask:/mnt/qrcode]# cp -p /mnt/libqrencode-arm-so/lib/libqrencode.so.4 /usr/lib/
[root@100ask:/mnt/qrcode]#

Execute again:

[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ./Qrcode
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]# ls
Makefile  Makefile2  QRTest  Qrcode  main.cpp  qrcode.bmp
[root@100ask:/mnt/qrcode]#
[root@100ask:/mnt/qrcode]#

Copy the pictures in the development board to see if they are successful:

Download succeeded

It can be seen that it can be successful:

5, Existing problems

The generated image is too small to find a solution.

It is understood that ffmpeg can be used to solve the problem of image size.

6, References

Installation test related:
https://blog.csdn.net/zb774095236/article/details/107938644/

Migration related:
https://www.cnblogs.com/kuang17/p/7825323.html

Compilation related:
https://blog.csdn.net/u014213012/article/details/51833299