├── .gitignore ├── src ├── qrbits.h ├── qrcode.h ├── qrencode.h ├── qrcode.cpp ├── frame.c └── qrencode.c ├── keywords.txt ├── examples ├── basic │ └── basic.ino └── wifi │ └── wifi.ino ├── README.md ├── library.properties └── library.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /src/qrbits.h: -------------------------------------------------------------------------------- 1 | #define QRBIT(x,y) ( ( qrframe[((x)>>3) + (y) * WDB] >> (7-((x) & 7 ))) & 1 ) 2 | #define SETQRBIT(x,y) qrframe[((x)>>3) + (y) * WDB] |= 0x80 >> ((x) & 7) 3 | #define TOGQRBIT(x,y) qrframe[((x)>>3) + (y) * WDB] ^= 0x80 >> ((x) & 7) 4 | -------------------------------------------------------------------------------- /src/qrcode.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class QRcode 4 | { 5 | private: 6 | TFT_eSPI *tft; 7 | void render(int x, int y, int color); 8 | 9 | public: 10 | QRcode(TFT_eSPI *display); 11 | void init(); 12 | void create(String message); 13 | }; 14 | -------------------------------------------------------------------------------- /src/qrencode.h: -------------------------------------------------------------------------------- 1 | #ifndef __AVR__ 2 | #define PROGMEM 3 | #define memcpy_P memcpy 4 | #define __LPM(x) *x 5 | #define pgm_read_word(x) *x 6 | #else 7 | #include 8 | #define USEPRECALC 9 | #endif 10 | 11 | extern unsigned char strinbuf[]; 12 | extern unsigned char qrframe[]; 13 | 14 | extern unsigned char WD, WDB; 15 | #include "qrbits.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C"{ 19 | #endif 20 | // strinbuf in, qrframe out 21 | void qrencode(void); 22 | #ifdef __cplusplus 23 | } // extern "C" 24 | #endif 25 | 26 | 27 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For QRCODE 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | QRcode KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | init KEYWORD2 14 | create KEYWORD2 15 | 16 | ####################################### 17 | # Constants (LITERAL1) 18 | ####################################### 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/basic/basic.ino: -------------------------------------------------------------------------------- 1 | /* ********************************************************************************* 2 | * 3 | * dependency library : 4 | * TFT_eSPI by Bodmer https://github.com/Bodmer/TFT_eSPI 5 | * 6 | ***********************************************************************************/ 7 | 8 | 9 | #include 10 | #include 11 | 12 | TFT_eSPI tft = TFT_eSPI(); 13 | QRcode qrcode (&tft); 14 | 15 | void setup() { 16 | 17 | tft.init(); 18 | tft.setRotation(3); 19 | 20 | qrcode.init(); 21 | // create qrcode 22 | qrcode.create("Hello world."); 23 | 24 | } 25 | 26 | void loop() { } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TFT_eSPI QRcode 2 | 3 | Arduino Library to generate QR codes for TFT_eSPI supported displays, ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D, ST7789 and ST7796 based TFT displays that support SPI or 8 bit parallel. 4 | 5 | Just copy the TFT_ESP_QRcode folder to your Arduino 'libraries' folder. 6 | 7 | The code is based on qrdquino by tz1 : https://github.com/tz1/qrduino 8 | 9 | This code is a fork of https://github.com/yoprogramo/ESP_QRcode 10 | 11 | ## Install dependency for TFT_eSPI QRcode 12 | Open Library Manager (menu Sketch > Include Library > Manage Libraries…) then install the following libraries 13 | - TFT_eSPI 14 | 15 | ## Run examples from TFT_eSPI_QRcode 16 | - Open Arduino IDE, try examples of TFT_eSPI QRcode library 17 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TFT_eSPI QRcode 2 | version=1.0.0 3 | author=Domenico Silletti Jose Antonio Espinosa and Anun Panya 4 | maintainer=Domenico Silletti 5 | sentence=Generate QRCode for TFT_eSPI suppoorted TFT displays, ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D, ST7789 and ST7796 based TFT displays that support SPI or 8 bit parallel 6 | paragraph=Generate QRCode for TFT_eSPI suppoorted TFT displays, ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D, ST7789 and ST7796 based TFT displays that support SPI or 8 bit parallel 7 | category=Display 8 | url=https://github.com/dsilletti/TFT_eSPI_QRcode 9 | architectures=* 10 | depends=TFT_eSPI 11 | -------------------------------------------------------------------------------- /examples/wifi/wifi.ino: -------------------------------------------------------------------------------- 1 | /* ********************************************************************************* 2 | * 3 | * dependency library : 4 | * TFT_eSPI by Bodmer https://github.com/Bodmer/TFT_eSPI 5 | * 6 | ***********************************************************************************/ 7 | 8 | 9 | #include 10 | #include 11 | 12 | TFT_eSPI tft = TFT_eSPI(); 13 | QRcode qrcode (&tft); 14 | 15 | String ssid = "testWiFi"; //Network SSID. Required. 16 | String security = "WPA2"; // "WEP", "WPA", "WPA2", "WPA3" or "nopass" for open 17 | String password = "password"; //Password, ignored if security is "nopass" 18 | 19 | void setup() { 20 | 21 | tft.init(); 22 | tft.setRotation(3); 23 | 24 | qrcode.init(); 25 | // create qrcode 26 | qrcode.create("WIFI:S:"+ssid+";T:"+security+";P:"+password+";;"); 27 | 28 | } 29 | 30 | void loop() { } -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TFT_eSPI QRcode", 3 | "version": "1.0.0", 4 | "keywords": "qr code, tft, TFT_eSPI, ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D, ST7789, ST7796", 5 | "description": "generate QR codes with TFT_eSPI supported displays, ILI9341, ILI9163, ST7735, S6D02A1, ILI9481, ILI9486, ILI9488, HX8357D, ST7789 and ST7796 based TFT displays that support SPI or 8 bit parallel", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/dsilletti/TFT_eSPI_QRcode" 10 | }, 11 | "authors": 12 | [ 13 | { 14 | "name": "Domenico Silletti", 15 | "email": "domenico@silletti.it", 16 | "url": "https://github.com/dsilletti" 17 | }, 18 | { 19 | "name": "Jose Antonio Espinosa", 20 | "email": "yoprogramo@gmail.com", 21 | "url": "https://yoprogramo.com" 22 | }, 23 | { 24 | "name": "Anun Panya", 25 | "email": "aspirinenun@gmail.com", 26 | "url": "https://www.facebook.com/anun.panya" 27 | } 28 | ], 29 | "frameworks": "arduino", 30 | "platforms": "espressif8266, espressif32, ststm32" 31 | } 32 | -------------------------------------------------------------------------------- /src/qrcode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "qrcode.h" 3 | #include "qrencode.h" 4 | 5 | int offsetsX; 6 | int offsetsY; 7 | int screenwidth; 8 | int screenheight; 9 | int multiply=2; 10 | 11 | QRcode::QRcode(TFT_eSPI *tft){ 12 | this->tft = tft; 13 | } 14 | 15 | void QRcode::init(){ 16 | //((TFT_eSPI *)tft)->init(); 17 | screenwidth = tft->width(); 18 | screenheight = tft->height(); 19 | //tft->setRotation(1); 20 | //tft->fillScreen(TFT_WHITE); 21 | int min = screenwidth; 22 | if (screenheightdrawPixel(x,y,TFT_BLACK); 35 | if (multiply>1) { 36 | tft->drawPixel(x+1,y,TFT_BLACK); 37 | tft->drawPixel(x+1,y+1,TFT_BLACK); 38 | tft->drawPixel(x,y+1,TFT_BLACK); 39 | } 40 | } 41 | else { 42 | tft->drawPixel(x,y,TFT_WHITE); 43 | if (multiply>1) { 44 | tft->drawPixel(x+1,y,TFT_WHITE); 45 | tft->drawPixel(x+1,y+1,TFT_WHITE); 46 | tft->drawPixel(x,y+1,TFT_WHITE); 47 | } 48 | } 49 | } 50 | 51 | void QRcode::create(String message) { 52 | // create QR code 53 | tft->fillScreen(TFT_WHITE); 54 | message.toCharArray((char *)strinbuf,260); 55 | qrencode(); 56 | // print QR Code 57 | for (byte x = 0; x < WD; x+=2) { 58 | for (byte y = 0; y < WD; y++) { 59 | if ( QRBIT(x,y) && QRBIT((x+1),y)) { 60 | // black square on top of black square 61 | render(x, y, 1); 62 | render((x+1), y, 1); 63 | } 64 | if (!QRBIT(x,y) && QRBIT((x+1),y)) { 65 | // white square on top of black square 66 | render(x, y, 0); 67 | render((x+1), y, 1); 68 | } 69 | if ( QRBIT(x,y) && !QRBIT((x+1),y)) { 70 | // black square on top of white square 71 | render(x, y, 1); 72 | render((x+1), y, 0); 73 | } 74 | if (!QRBIT(x,y) && !QRBIT((x+1),y)) { 75 | // white square on top of white square 76 | render(x, y, 0); 77 | render((x+1), y, 0); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/frame.c: -------------------------------------------------------------------------------- 1 | const unsigned char neccblk1 = 2; 2 | const unsigned char neccblk2 = 0; 3 | const unsigned char datablkw = 78; 4 | const unsigned char eccblkwid = 20; 5 | const unsigned char VERSION = 7; 6 | const unsigned char ECCLEVEL = 1; 7 | const unsigned char WD = 45; 8 | const unsigned char WDB = 6; 9 | unsigned char strinbuf[270]; 10 | unsigned char qrframe[600]; 11 | unsigned char rlens[46]; 12 | #ifndef __AVR__ 13 | #define PROGMEM 14 | #define memcpy_P memcpy 15 | #define __LPM(x) *x 16 | #else 17 | #include 18 | #endif 19 | const unsigned char framebase[] = { 20 | 0xfe,0x00,0x00,0x00,0x0b,0xf8, 21 | 0x82,0x00,0x00,0x00,0x12,0x08, 22 | 0xba,0x00,0x00,0x00,0x12,0xe8, 23 | 0xba,0x00,0x00,0x00,0x1a,0xe8, 24 | 0xba,0x00,0x0f,0x80,0x3a,0xe8, 25 | 0x82,0x00,0x08,0x80,0x02,0x08, 26 | 0xfe,0xaa,0xaa,0xaa,0xab,0xf8, 27 | 0x00,0x00,0x08,0x80,0x00,0x00, 28 | 0x02,0x00,0x0f,0x80,0x00,0x00, 29 | 0x00,0x00,0x00,0x00,0x00,0x00, 30 | 0x02,0x00,0x00,0x00,0x00,0x00, 31 | 0x00,0x00,0x00,0x00,0x00,0x00, 32 | 0x02,0x00,0x00,0x00,0x00,0x00, 33 | 0x00,0x00,0x00,0x00,0x00,0x00, 34 | 0x02,0x00,0x00,0x00,0x00,0x00, 35 | 0x00,0x00,0x00,0x00,0x00,0x00, 36 | 0x02,0x00,0x00,0x00,0x00,0x00, 37 | 0x00,0x00,0x00,0x00,0x00,0x00, 38 | 0x02,0x00,0x00,0x00,0x00,0x00, 39 | 0x00,0x00,0x00,0x00,0x00,0x00, 40 | 0x0f,0x80,0x0f,0x80,0x0f,0x80, 41 | 0x08,0x80,0x08,0x80,0x08,0x80, 42 | 0x0a,0x80,0x0a,0x80,0x0a,0x80, 43 | 0x08,0x80,0x08,0x80,0x08,0x80, 44 | 0x0f,0x80,0x0f,0x80,0x0f,0x80, 45 | 0x00,0x00,0x00,0x00,0x00,0x00, 46 | 0x02,0x00,0x00,0x00,0x00,0x00, 47 | 0x00,0x00,0x00,0x00,0x00,0x00, 48 | 0x02,0x00,0x00,0x00,0x00,0x00, 49 | 0x00,0x00,0x00,0x00,0x00,0x00, 50 | 0x02,0x00,0x00,0x00,0x00,0x00, 51 | 0x00,0x00,0x00,0x00,0x00,0x00, 52 | 0x02,0x00,0x00,0x00,0x00,0x00, 53 | 0x00,0x00,0x00,0x00,0x00,0x00, 54 | 0x0a,0x00,0x00,0x00,0x00,0x00, 55 | 0x78,0x00,0x00,0x00,0x00,0x00, 56 | 0x9a,0x00,0x0f,0x80,0x0f,0x80, 57 | 0x00,0x80,0x08,0x80,0x08,0x80, 58 | 0xfe,0x00,0x0a,0x80,0x0a,0x80, 59 | 0x82,0x00,0x08,0x80,0x08,0x80, 60 | 0xba,0x00,0x0f,0x80,0x0f,0x80, 61 | 0xba,0x00,0x00,0x00,0x00,0x00, 62 | 0xba,0x00,0x00,0x00,0x00,0x00, 63 | 0x82,0x00,0x00,0x00,0x00,0x00, 64 | 0xfe,0x00,0x00,0x00,0x00,0x00, 65 | }; 66 | 67 | const unsigned char framask[] = { 68 | 0xff,0xff,0xff,0xff,0xff,0xf8, 69 | 0x10,0x04,0x00,0x80,0x08,0x00, 70 | 0x40,0x01,0x00,0x02,0x00,0x02, 71 | 0x00,0x01,0x00,0x00,0x40,0x00, 72 | 0x08,0x00,0x03,0xe0,0x02,0x1f, 73 | 0x00,0x18,0x7c,0x00,0x70,0xf8, 74 | 0x00,0xf0,0xf8,0x00,0xf8,0x10, 75 | 0x00,0x00,0x04,0x00,0x00,0x00, 76 | 0x80,0x00,0x00,0x08,0x00,0x00, 77 | 0x00,0x40,0x00,0x00,0x01,0x00, 78 | 0x00,0x00,0x02,0x00,0x00,0x00, 79 | 0x02,0x00,0x00,0x00,0x01,0x00, 80 | 0x00,0x00,0x1f,0xc0,0x00,0x00, 81 | 0x03,0xf8,0x00,0x00,0x00,0x3f, 82 | 0x80,0x03,0xe0,0x03,0xff,0x00, 83 | 0x1f,0x00,0x1f,0xfc,0x00,0x7c, 84 | 0x00,0x7f,0xf8,0x00,0xf8,0x00, 85 | 0xff,0xf8,0x00,0xf8,0x00,0xff, 86 | 0xfc,0x00,0x00,0x00,0x01,0xff, 87 | 0x00,0x00,0x00,0x00,0x3f,0xe0, 88 | 0x00,0x00,0x00,0x03,0xfe,0x00, 89 | 0x00,0x00,0x00,0x00, 90 | }; 91 | -------------------------------------------------------------------------------- /src/qrencode.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "qrencode.h" 4 | 5 | extern unsigned char neccblk1; 6 | extern unsigned char neccblk2; 7 | extern unsigned char datablkw; 8 | extern unsigned char eccblkwid; 9 | extern unsigned char VERSION; 10 | extern unsigned char ECCLEVEL; 11 | extern unsigned char WD, WDB; 12 | extern unsigned char rlens[]; 13 | extern unsigned char framebase[]; 14 | extern unsigned char framask[]; 15 | 16 | //======================================================================== 17 | // Reed Solomon error correction 18 | unsigned modnn(unsigned x) 19 | { 20 | while (x >= 255) { 21 | x -= 255; 22 | x = (x >> 8) + (x & 255); 23 | } 24 | return x; 25 | } 26 | 27 | #ifndef RTGENEXPLOG 28 | const unsigned char g0log[256] = { 29 | 0xff,0x00,0x01,0x19,0x02,0x32,0x1a,0xc6,0x03,0xdf,0x33,0xee,0x1b,0x68,0xc7,0x4b, 30 | 0x04,0x64,0xe0,0x0e,0x34,0x8d,0xef,0x81,0x1c,0xc1,0x69,0xf8,0xc8,0x08,0x4c,0x71, 31 | 0x05,0x8a,0x65,0x2f,0xe1,0x24,0x0f,0x21,0x35,0x93,0x8e,0xda,0xf0,0x12,0x82,0x45, 32 | 0x1d,0xb5,0xc2,0x7d,0x6a,0x27,0xf9,0xb9,0xc9,0x9a,0x09,0x78,0x4d,0xe4,0x72,0xa6, 33 | 0x06,0xbf,0x8b,0x62,0x66,0xdd,0x30,0xfd,0xe2,0x98,0x25,0xb3,0x10,0x91,0x22,0x88, 34 | 0x36,0xd0,0x94,0xce,0x8f,0x96,0xdb,0xbd,0xf1,0xd2,0x13,0x5c,0x83,0x38,0x46,0x40, 35 | 0x1e,0x42,0xb6,0xa3,0xc3,0x48,0x7e,0x6e,0x6b,0x3a,0x28,0x54,0xfa,0x85,0xba,0x3d, 36 | 0xca,0x5e,0x9b,0x9f,0x0a,0x15,0x79,0x2b,0x4e,0xd4,0xe5,0xac,0x73,0xf3,0xa7,0x57, 37 | 0x07,0x70,0xc0,0xf7,0x8c,0x80,0x63,0x0d,0x67,0x4a,0xde,0xed,0x31,0xc5,0xfe,0x18, 38 | 0xe3,0xa5,0x99,0x77,0x26,0xb8,0xb4,0x7c,0x11,0x44,0x92,0xd9,0x23,0x20,0x89,0x2e, 39 | 0x37,0x3f,0xd1,0x5b,0x95,0xbc,0xcf,0xcd,0x90,0x87,0x97,0xb2,0xdc,0xfc,0xbe,0x61, 40 | 0xf2,0x56,0xd3,0xab,0x14,0x2a,0x5d,0x9e,0x84,0x3c,0x39,0x53,0x47,0x6d,0x41,0xa2, 41 | 0x1f,0x2d,0x43,0xd8,0xb7,0x7b,0xa4,0x76,0xc4,0x17,0x49,0xec,0x7f,0x0c,0x6f,0xf6, 42 | 0x6c,0xa1,0x3b,0x52,0x29,0x9d,0x55,0xaa,0xfb,0x60,0x86,0xb1,0xbb,0xcc,0x3e,0x5a, 43 | 0xcb,0x59,0x5f,0xb0,0x9c,0xa9,0xa0,0x51,0x0b,0xf5,0x16,0xeb,0x7a,0x75,0x2c,0xd7, 44 | 0x4f,0xae,0xd5,0xe9,0xe6,0xe7,0xad,0xe8,0x74,0xd6,0xf4,0xea,0xa8,0x50,0x58,0xaf, 45 | }; 46 | 47 | const unsigned char g0exp[256] = { 48 | 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1d,0x3a,0x74,0xe8,0xcd,0x87,0x13,0x26, 49 | 0x4c,0x98,0x2d,0x5a,0xb4,0x75,0xea,0xc9,0x8f,0x03,0x06,0x0c,0x18,0x30,0x60,0xc0, 50 | 0x9d,0x27,0x4e,0x9c,0x25,0x4a,0x94,0x35,0x6a,0xd4,0xb5,0x77,0xee,0xc1,0x9f,0x23, 51 | 0x46,0x8c,0x05,0x0a,0x14,0x28,0x50,0xa0,0x5d,0xba,0x69,0xd2,0xb9,0x6f,0xde,0xa1, 52 | 0x5f,0xbe,0x61,0xc2,0x99,0x2f,0x5e,0xbc,0x65,0xca,0x89,0x0f,0x1e,0x3c,0x78,0xf0, 53 | 0xfd,0xe7,0xd3,0xbb,0x6b,0xd6,0xb1,0x7f,0xfe,0xe1,0xdf,0xa3,0x5b,0xb6,0x71,0xe2, 54 | 0xd9,0xaf,0x43,0x86,0x11,0x22,0x44,0x88,0x0d,0x1a,0x34,0x68,0xd0,0xbd,0x67,0xce, 55 | 0x81,0x1f,0x3e,0x7c,0xf8,0xed,0xc7,0x93,0x3b,0x76,0xec,0xc5,0x97,0x33,0x66,0xcc, 56 | 0x85,0x17,0x2e,0x5c,0xb8,0x6d,0xda,0xa9,0x4f,0x9e,0x21,0x42,0x84,0x15,0x2a,0x54, 57 | 0xa8,0x4d,0x9a,0x29,0x52,0xa4,0x55,0xaa,0x49,0x92,0x39,0x72,0xe4,0xd5,0xb7,0x73, 58 | 0xe6,0xd1,0xbf,0x63,0xc6,0x91,0x3f,0x7e,0xfc,0xe5,0xd7,0xb3,0x7b,0xf6,0xf1,0xff, 59 | 0xe3,0xdb,0xab,0x4b,0x96,0x31,0x62,0xc4,0x95,0x37,0x6e,0xdc,0xa5,0x57,0xae,0x41, 60 | 0x82,0x19,0x32,0x64,0xc8,0x8d,0x07,0x0e,0x1c,0x38,0x70,0xe0,0xdd,0xa7,0x53,0xa6, 61 | 0x51,0xa2,0x59,0xb2,0x79,0xf2,0xf9,0xef,0xc3,0x9b,0x2b,0x56,0xac,0x45,0x8a,0x09, 62 | 0x12,0x24,0x48,0x90,0x3d,0x7a,0xf4,0xf5,0xf7,0xf3,0xfb,0xeb,0xcb,0x8b,0x0b,0x16, 63 | 0x2c,0x58,0xb0,0x7d,0xfa,0xe9,0xcf,0x83,0x1b,0x36,0x6c,0xd8,0xad,0x47,0x8e,0x00, 64 | }; 65 | #define glog(x) __LPM(&g0log[x]) 66 | #define gexp(x) __LPM(&g0exp[x]) 67 | 68 | #else // generate the log and exp tables - some CPU, much less flash, +512 bytes ram which can be shared 69 | 70 | unsigned char g0log[256],g0exp[256]; 71 | #define glog(x) (g0log[x]) 72 | #define gexp(x) (g0exp[x]) 73 | void gentables() { 74 | #define GFPOLY (0x11d) 75 | unsigned char i,j; 76 | g0log[0] = 255; 77 | g0exp[255] = 0; 78 | j = 1; 79 | for (i = 0; i < 255; i++) { 80 | g0log[j] = i; 81 | g0exp[i] = j; 82 | j <<= 1; 83 | if (j & 256) 84 | j ^= GFPOLY; 85 | j &= 255; 86 | } 87 | } 88 | #endif 89 | 90 | void initrspoly(unsigned char eclen, unsigned char *genpoly) 91 | { 92 | unsigned char i, j; 93 | 94 | #ifdef RTGENEXPLOG 95 | gentables(); 96 | #endif 97 | 98 | genpoly[0] = 1; 99 | for (i = 0; i < eclen; i++) { 100 | genpoly[i + 1] = 1; 101 | for (j = i; j > 0; j--) 102 | genpoly[j] = genpoly[j] 103 | ? genpoly[j - 1] ^ gexp(modnn(glog(genpoly[j]) + i)) : genpoly[j - 1]; 104 | genpoly[0] = gexp(modnn(glog(genpoly[0]) + i)); 105 | } 106 | for (i = 0; i <= eclen; i++) 107 | genpoly[i] = glog(genpoly[i]); // use logs for genpoly[] 108 | } 109 | 110 | void appendrs(unsigned char *data, unsigned char dlen, 111 | unsigned char *ecbuf, unsigned char eclen, unsigned char *genpoly) 112 | { 113 | unsigned char i, j, fb; 114 | 115 | memset(ecbuf, 0, eclen); 116 | for (i = 0; i < dlen; i++) { 117 | fb = glog(data[i] ^ ecbuf[0]); 118 | if (fb != 255) /* fb term is non-zero */ 119 | for (j = 1; j < eclen; j++) 120 | ecbuf[j-1] = ecbuf[j] ^ gexp(modnn(fb + genpoly[eclen - j])); 121 | else 122 | memmove(ecbuf, ecbuf + 1, eclen - 1); 123 | ecbuf[eclen - 1] = fb == 255 ? 0 : gexp(modnn(fb + genpoly[0])); 124 | } 125 | } 126 | 127 | //======================================================================== 128 | // 8 bit data to QR-coded 8 bit data 129 | void stringtoqr(void) 130 | { 131 | unsigned i; 132 | unsigned size, max; 133 | size = strlen((char *) strinbuf); 134 | 135 | max = datablkw * (neccblk1 + neccblk2) + neccblk2; 136 | if (size >= max - 2) { 137 | size = max - 2; 138 | if (VERSION > 9) 139 | size--; 140 | } 141 | 142 | i = size; 143 | if (VERSION > 9) { 144 | strinbuf[i + 2] = 0; 145 | while (i--) { 146 | strinbuf[i + 3] |= strinbuf[i] << 4; 147 | strinbuf[i + 2] = strinbuf[i] >> 4; 148 | } 149 | strinbuf[2] |= size << 4; 150 | strinbuf[1] = size >> 4; 151 | strinbuf[0] = 0x40 | (size >> 12); 152 | } else { 153 | strinbuf[i + 1] = 0; 154 | while (i--) { 155 | strinbuf[i + 2] |= strinbuf[i] << 4; 156 | strinbuf[i + 1] = strinbuf[i] >> 4; 157 | } 158 | strinbuf[1] |= size << 4; 159 | strinbuf[0] = 0x40 | (size >> 4); 160 | } 161 | i = size + 3 - (VERSION < 10); 162 | while (i < max) { 163 | strinbuf[i++] = 0xec; 164 | // buffer has room if (i == max) break; 165 | strinbuf[i++] = 0x11; 166 | } 167 | 168 | // calculate and append ECC 169 | unsigned char *ecc = &strinbuf[max]; 170 | unsigned char *dat = strinbuf; 171 | initrspoly(eccblkwid,qrframe); 172 | 173 | for (i = 0; i < neccblk1; i++) { 174 | appendrs(dat, datablkw, ecc, eccblkwid, qrframe); 175 | dat += datablkw; 176 | ecc += eccblkwid; 177 | } 178 | for (i = 0; i < neccblk2; i++) { 179 | appendrs(dat, datablkw + 1, ecc, eccblkwid, qrframe); 180 | dat += datablkw + 1; 181 | ecc += eccblkwid; 182 | } 183 | unsigned j; 184 | dat = qrframe; 185 | for (i = 0; i < datablkw; i++) { 186 | for (j = 0; j < neccblk1; j++) 187 | *dat++ = strinbuf[i + j * datablkw]; 188 | for (j = 0; j < neccblk2; j++) 189 | *dat++ = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))]; 190 | } 191 | for (j = 0; j < neccblk2; j++) 192 | *dat++ = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))]; 193 | for (i = 0; i < eccblkwid; i++) 194 | for (j = 0; j < neccblk1 + neccblk2; j++) 195 | *dat++ = strinbuf[max + i + j * eccblkwid]; 196 | memcpy(strinbuf, qrframe, max + eccblkwid * (neccblk1 + neccblk2)); 197 | 198 | } 199 | 200 | //======================================================================== 201 | // Frame data insert following the path rules 202 | unsigned char ismasked(unsigned char x, unsigned char y) 203 | { 204 | unsigned bt; 205 | if (x > y) { 206 | bt = x; 207 | x = y; 208 | y = bt; 209 | } 210 | bt = y; 211 | bt += y * y; 212 | #if 0 213 | // bt += y*y; 214 | unsigned s = 1; 215 | while (y--) { 216 | bt += s; 217 | s += 2; 218 | } 219 | #endif 220 | bt >>= 1; 221 | bt += x; 222 | return (__LPM(&framask[bt >> 3]) >> (7 - (bt & 7))) & 1; 223 | } 224 | 225 | void fillframe(void) 226 | { 227 | unsigned i; 228 | unsigned char d, j; 229 | unsigned char x, y, ffdecy, ffgohv; 230 | 231 | memcpy_P(qrframe, framebase, WDB * WD); 232 | x = y = WD - 1; 233 | ffdecy = 1; // up, minus 234 | ffgohv = 1; 235 | 236 | /* inteleaved data and ecc codes */ 237 | for (i = 0; i < ((datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2); i++) { 238 | d = strinbuf[i]; 239 | for (j = 0; j < 8; j++, d <<= 1) { 240 | if (0x80 & d) 241 | SETQRBIT(x, y); 242 | do { // find next fill position 243 | if (ffgohv) 244 | x--; 245 | else { 246 | x++; 247 | if (ffdecy) { 248 | if (y != 0) 249 | y--; 250 | else { 251 | x -= 2; 252 | ffdecy = !ffdecy; 253 | if (x == 6) { 254 | x--; 255 | y = 9; 256 | } 257 | } 258 | } else { 259 | if (y != WD - 1) 260 | y++; 261 | else { 262 | x -= 2; 263 | ffdecy = !ffdecy; 264 | if (x == 6) { 265 | x--; 266 | y -= 8; 267 | } 268 | } 269 | } 270 | } 271 | ffgohv = !ffgohv; 272 | } while (ismasked(x, y)); 273 | } 274 | } 275 | 276 | } 277 | 278 | //======================================================================== 279 | // Masking 280 | void applymask(unsigned char m) 281 | { 282 | unsigned char x, y, r3x, r3y; 283 | 284 | switch (m) { 285 | case 0: 286 | for (y = 0; y < WD; y++) 287 | for (x = 0; x < WD; x++) 288 | if (!((x + y) & 1) && !ismasked(x, y)) 289 | TOGQRBIT(x, y); 290 | break; 291 | case 1: 292 | for (y = 0; y < WD; y++) 293 | for (x = 0; x < WD; x++) 294 | if (!(y & 1) && !ismasked(x, y)) 295 | TOGQRBIT(x, y); 296 | break; 297 | case 2: 298 | for (y = 0; y < WD; y++) 299 | for (r3x = 0, x = 0; x < WD; x++, r3x++) { 300 | if (r3x == 3) 301 | r3x = 0; 302 | if (!r3x && !ismasked(x, y)) 303 | TOGQRBIT(x, y); 304 | } 305 | break; 306 | case 3: 307 | for (r3y = 0, y = 0; y < WD; y++, r3y++) { 308 | if (r3y == 3) 309 | r3y = 0; 310 | for (r3x = r3y, x = 0; x < WD; x++, r3x++) { 311 | if (r3x == 3) 312 | r3x = 0; 313 | if (!r3x && !ismasked(x, y)) 314 | TOGQRBIT(x, y); 315 | } 316 | } 317 | break; 318 | case 4: 319 | for (y = 0; y < WD; y++) 320 | for (r3x = 0, r3y = ((y >> 1) & 1), x = 0; x < WD; x++, r3x++) { 321 | if (r3x == 3) { 322 | r3x = 0; 323 | r3y = !r3y; 324 | } 325 | if (!r3y && !ismasked(x, y)) 326 | TOGQRBIT(x, y); 327 | } 328 | break; 329 | case 5: 330 | for (r3y = 0, y = 0; y < WD; y++, r3y++) { 331 | if (r3y == 3) 332 | r3y = 0; 333 | for (r3x = 0, x = 0; x < WD; x++, r3x++) { 334 | if (r3x == 3) 335 | r3x = 0; 336 | if (!((x & y & 1) + !(!r3x | !r3y)) && !ismasked(x, y)) 337 | TOGQRBIT(x, y); 338 | } 339 | } 340 | break; 341 | case 6: 342 | for (r3y = 0, y = 0; y < WD; y++, r3y++) { 343 | if (r3y == 3) 344 | r3y = 0; 345 | for (r3x = 0, x = 0; x < WD; x++, r3x++) { 346 | if (r3x == 3) 347 | r3x = 0; 348 | if (!(((x & y & 1) + (r3x && (r3x == r3y))) & 1) && !ismasked(x, y)) 349 | TOGQRBIT(x, y); 350 | } 351 | } 352 | break; 353 | case 7: 354 | for (r3y = 0, y = 0; y < WD; y++, r3y++) { 355 | if (r3y == 3) 356 | r3y = 0; 357 | for (r3x = 0, x = 0; x < WD; x++, r3x++) { 358 | if (r3x == 3) 359 | r3x = 0; 360 | if (!(((r3x && (r3x == r3y)) + ((x + y) & 1)) & 1) && !ismasked(x, y)) 361 | TOGQRBIT(x, y); 362 | } 363 | } 364 | break; 365 | } 366 | return; 367 | } 368 | 369 | // Badness coefficients. 370 | const unsigned char N1 = 3; 371 | const unsigned char N2 = 3; 372 | const unsigned char N3 = 40; 373 | const unsigned char N4 = 10; 374 | 375 | unsigned badruns(unsigned char length) 376 | { 377 | unsigned char i; 378 | unsigned runsbad = 0; 379 | for (i = 0; i <= length; i++) 380 | if (rlens[i] >= 5) 381 | runsbad += N1 + rlens[i] - 5; 382 | // BwBBBwB 383 | for (i = 3; i < length - 1; i += 2) 384 | if (rlens[i - 2] == rlens[i + 2] 385 | && rlens[i + 2] == rlens[i - 1] 386 | && rlens[i - 1] == rlens[i + 1] 387 | && rlens[i - 1] * 3 == rlens[i] 388 | // white around the black pattern? Not part of spec 389 | && (rlens[i - 3] == 0 // beginning 390 | || i + 3 > length // end 391 | || rlens[i - 3] * 3 >= rlens[i] * 4 || rlens[i + 3] * 3 >= rlens[i] * 4) 392 | ) 393 | runsbad += N3; 394 | return runsbad; 395 | } 396 | 397 | int badcheck() 398 | { 399 | unsigned char x, y, h, b, b1; 400 | unsigned thisbad = 0; 401 | int bw = 0; 402 | 403 | // blocks of same color. 404 | for (y = 0; y < WD - 1; y++) 405 | for (x = 0; x < WD - 1; x++) 406 | if ((QRBIT(x, y) && QRBIT(x + 1, y) && QRBIT(x, y + 1) && QRBIT(x + 1, y + 1)) // all black 407 | || !(QRBIT(x, y) || QRBIT(x + 1, y) || QRBIT(x, y + 1) || QRBIT(x + 1, y + 1))) // all white 408 | thisbad += N2; 409 | 410 | // X runs 411 | for (y = 0; y < WD; y++) { 412 | rlens[0] = 0; 413 | for (h = b = x = 0; x < WD; x++) { 414 | if ((b1 = QRBIT(x, y)) == b) 415 | rlens[h]++; 416 | else 417 | rlens[++h] = 1; 418 | b = b1; 419 | bw += b ? 1 : -1; 420 | } 421 | thisbad += badruns(h); 422 | } 423 | 424 | // black/white imbalance 425 | if (bw < 0) 426 | bw = -bw; 427 | 428 | unsigned long big = bw; 429 | unsigned count = 0; 430 | big += big << 2; 431 | big <<= 1; 432 | while (big > WD * WD) 433 | big -= WD * WD, count++; 434 | thisbad += count * N4; 435 | 436 | // Y runs 437 | for (x = 0; x < WD; x++) { 438 | rlens[0] = 0; 439 | for (h = b = y = 0; y < WD; y++) { 440 | if ((b1 = QRBIT(x, y)) == b) 441 | rlens[h]++; 442 | else 443 | rlens[++h] = 1; 444 | b = b1; 445 | } 446 | thisbad += badruns(h); 447 | } 448 | return thisbad; 449 | } 450 | 451 | // final format bits with mask 452 | // level << 3 | mask 453 | const unsigned fmtword[] = { 454 | 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, //L 455 | 0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, //M 456 | 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed, //Q 457 | 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b, //H 458 | }; 459 | 460 | void addfmt(unsigned char masknum) 461 | { 462 | unsigned fmtbits; 463 | unsigned char i, lvl = ECCLEVEL - 1; 464 | 465 | fmtbits = pgm_read_word(&fmtword[masknum + (lvl << 3)]); 466 | // low byte 467 | for (i = 0; i < 8; i++, fmtbits >>= 1) 468 | if (fmtbits & 1) { 469 | SETQRBIT(WD - 1 - i, 8); 470 | if (i < 6) 471 | SETQRBIT(8, i); 472 | else 473 | SETQRBIT(8, i + 1); 474 | } 475 | // high byte 476 | for (i = 0; i < 7; i++, fmtbits >>= 1) 477 | if (fmtbits & 1) { 478 | SETQRBIT(8, WD - 7 + i); 479 | if (i) 480 | SETQRBIT(6 - i, 8); 481 | else 482 | SETQRBIT(7, 8); 483 | } 484 | } 485 | 486 | void qrencode() 487 | { 488 | unsigned mindem = 30000; 489 | unsigned char best = 0; 490 | unsigned char i; 491 | unsigned badness; 492 | 493 | stringtoqr(); 494 | fillframe(); // Inisde loop to avoid having separate mask buffer 495 | memcpy(strinbuf, qrframe, WD * WDB); 496 | for (i = 0; i < 8; i++) { 497 | applymask(i); // returns black-white imbalance 498 | badness = badcheck(); 499 | #if 0 //ndef PUREBAD 500 | if (badness < WD * WD * 5 / 4) { // good enough - masks grow in compute complexity 501 | best = i; 502 | break; 503 | } 504 | #endif 505 | if (badness < mindem) { 506 | mindem = badness; 507 | best = i; 508 | } 509 | if (best == 7) 510 | break; // don't increment i to avoid redoing mask 511 | memcpy(qrframe, strinbuf, WD * WDB); // reset filled frame 512 | } 513 | if (best != i) // redo best mask - none good enough, last wasn't best 514 | applymask(best); 515 | addfmt(best); // add in final format bytes 516 | } 517 | --------------------------------------------------------------------------------