├── src ├── img │ └── qrcode.JPG ├── qrbits.h ├── qrcode.h ├── qrencode.h ├── qrcode.cpp ├── frame.c └── qrencode.c ├── component.mk ├── CMakeLists.txt ├── library.properties ├── library.json ├── README.md └── examples └── example └── example.ino /src/img/qrcode.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anunpanya/ESP8266_QRcode/HEAD/src/img/qrcode.JPG -------------------------------------------------------------------------------- /component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_ADD_INCLUDEDIRS := src 2 | COMPONENT_SRCDIRS := src 3 | CXXFLAGS += -Wno-ignored-qualifiers 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_ADD_INCLUDEDIRS src) 2 | set(COMPONENT_PRIV_REQUIRES esp8266-oled-ssd1306) 3 | set(COMPONENT_SRCDIRS src) 4 | register_component() 5 | -------------------------------------------------------------------------------- /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 "OLEDDisplay.h" 2 | 3 | class QRcode 4 | { 5 | private: 6 | OLEDDisplay *display; 7 | void render(int x, int y, int color); 8 | 9 | public: 10 | QRcode(OLEDDisplay *display); 11 | 12 | void init(); 13 | void debug(); 14 | void screenwhite(); 15 | void create(String message); 16 | }; 17 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP8266 QRcode 2 | version=2.0.0 3 | author=Anun Panya 4 | maintainer=Anun Panya 5 | sentence=ESP8266 Generate QRCode for SSD1306 oled displays 128*64 pixel 6 | paragraph=ESP8266 Generate QRCode version 7 for SSD1306 oled displays 128*64 pixel 7 | category=Display 8 | url=https://github.com/anunpanya/ESP8266_QRcode 9 | architectures=esp8266 10 | depends=ESP8266 and ESP32 OLED driver for SSD1306 displays 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP8266 QRcode", 3 | "version": "2.0.0", 4 | "keywords": "ssd1306, oled, display, i2c, qr code", 5 | "description": "ESP8266 Generate QRCode for SSD1306 oled displays 128*64 pixel", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/anunpanya/ESP8266_QRcode.git" 10 | }, 11 | "authors": 12 | [ 13 | { 14 | "name": "Anun Panya", 15 | "email": "aspirinenun@gmail.com", 16 | "url": "https://www.facebook.com/anun.panya" 17 | } 18 | ], 19 | "frameworks": "arduino", 20 | "platforms": "espressif" 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266 QRcode 2 | 3 | Just copy the ESP8266 QRcode folder to your Arduino 'libraries' folder. I have included an example sketch. 4 | 5 | The code is based on qrdquino by tz1 : https://github.com/tz1/qrduino 6 | 7 | ## Install dependency for ESP8266 QRcode 8 | Open Library Manager (menu Sketch > Include Library > Manage Libraries…) then install the following libraries 9 | - ESP8266 Oled Driver for SSD1306 display by Daniel Eichborn, Fabrice Weinberg 10 | 11 | ## Run examples from ESP8266 QRcode 12 | - Open Arduino IDE, try examples of ESP8266 QRcode library 13 | 14 | ## Display example 15 | ![qrcode](src/img/qrcode.JPG?raw=true) 16 | -------------------------------------------------------------------------------- /examples/example/example.ino: -------------------------------------------------------------------------------- 1 | /* ********************************************************************************* 2 | * ESP8266 QRcode 3 | * dependency library : 4 | * ESP8266 Oled Driver for SSD1306 display by Daniel Eichborn, Fabrice Weinberg 5 | * 6 | * SDA --> D6 7 | * SCL --> D7 8 | ***********************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | SSD1306 display(0x3c, D6, D7); 14 | QRcode qrcode (&display); 15 | 16 | void setup() { 17 | 18 | Serial.begin(115200); 19 | Serial.println(""); 20 | Serial.println("Starting..."); 21 | 22 | display.init(); 23 | display.clear(); 24 | display.display(); 25 | 26 | 27 | // enable debug qrcode 28 | // qrcode.debug(); 29 | 30 | // Initialize QRcode display using library 31 | qrcode.init(); 32 | // create qrcode 33 | qrcode.create("Hello world."); 34 | 35 | } 36 | 37 | void loop() { } 38 | -------------------------------------------------------------------------------- /src/qrcode.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "qrcode.h" 3 | #include "qrencode.h" 4 | 5 | int offsetsX = 42; 6 | int offsetsY = 10; 7 | int screenwidth = 128; 8 | int screenheight = 64; 9 | bool QRDEBUG = false; 10 | 11 | QRcode::QRcode(OLEDDisplay *display){ 12 | this->display = display; 13 | } 14 | 15 | void QRcode::init(){ 16 | if(QRDEBUG) 17 | Serial.println("QRcode init"); 18 | 19 | display->init(); 20 | display->flipScreenVertically(); 21 | display->setColor(WHITE); 22 | } 23 | 24 | void QRcode::debug(){ 25 | QRDEBUG = true; 26 | } 27 | 28 | void QRcode::render(int x, int y, int color){ 29 | x=x+offsetsX; 30 | y=y+offsetsY; 31 | if(color==1) { 32 | display->setColor(BLACK); 33 | display->setPixel(x, y); 34 | } 35 | else { 36 | display->setColor(WHITE); 37 | display->setPixel(x, y); 38 | } 39 | } 40 | 41 | void QRcode::screenwhite(){ 42 | display->clear(); 43 | display->setColor(WHITE); 44 | display->fillRect(0, 0, screenwidth, screenheight); 45 | display->display(); 46 | } 47 | 48 | void QRcode::create(String message) { 49 | 50 | // create QR code 51 | message.toCharArray((char *)strinbuf,260); 52 | qrencode(); 53 | screenwhite(); 54 | 55 | if(QRDEBUG){ 56 | Serial.println("QRcode render"); 57 | Serial.println(); 58 | } 59 | // print QR Code 60 | for (byte x = 0; x < WD; x+=2) { 61 | for (byte y = 0; y < WD; y++) { 62 | if ( QRBIT(x,y) && QRBIT((x+1),y)) { 63 | // black square on top of black square 64 | if(QRDEBUG) 65 | Serial.print("@"); 66 | render(x, y, 1); 67 | render((x+1), y, 1); 68 | } 69 | if (!QRBIT(x,y) && QRBIT((x+1),y)) { 70 | // white square on top of black square 71 | if(QRDEBUG) 72 | Serial.print(" "); 73 | render(x, y, 0); 74 | render((x+1), y, 1); 75 | } 76 | if ( QRBIT(x,y) && !QRBIT((x+1),y)) { 77 | // black square on top of white square 78 | if(QRDEBUG) 79 | Serial.print("@"); 80 | render(x, y, 1); 81 | render((x+1), y, 0); 82 | } 83 | if (!QRBIT(x,y) && !QRBIT((x+1),y)) { 84 | // white square on top of white square 85 | if(QRDEBUG) 86 | Serial.print(" "); 87 | render(x, y, 0); 88 | render((x+1), y, 0); 89 | } 90 | } 91 | Serial.println(); 92 | } 93 | Serial.println(); 94 | display->display(); 95 | } 96 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------