├── .gitignore ├── LICENSE ├── README.md ├── README_ZH.md ├── README_offical.md ├── SConscript ├── examples └── QRCode │ ├── QRCode.ino │ └── QRCode_halfsize.ino ├── figures └── qrcode.png ├── qrcode.c ├── qrcode.h └── qrcode_sample.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 The packages repositories of RT-Thread. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qrcode 2 | 3 | [中文页](README_ZH.md) | English | [Offical](README_offical.md) 4 | 5 | ## 1 Introduction 6 | 7 | **qrcode** is a software package used to generate a QR code from a string. This software package is a port of **RT-Thread** based on [ricmoo/QRCode](https://github.com/ricmoo/QRCode) open source library. 8 | 9 | ### 1.1 Directory structure 10 | 11 | | Name | Description | 12 | | ---- | ---- | 13 | | samples | Examples directory, and some corresponding instructions | 14 | | examples | Arduino | 15 | | inc | Header file directory | 16 | | src | Source Code Directory | 17 | 18 | ### 1.2 License 19 | 20 | The `qrcode` software package extends the `QRCode` software package license agreement, please see the `qrcode/LICENSE` file. 21 | 22 | ### 1.3 Dependency 23 | 24 | - RT-Thread 3.0+ 25 | 26 | ## 2. How to open qrcode 27 | 28 | To use qrcodepackage, you need to select it in the package manager of RT-Thread. The specific path is as follows: 29 | 30 | ```shell 31 | RT-Thread online packages 32 | tools packages ---> 33 | [*] qrcode: A simple library for generating QR codes in C 34 | [*] Enable qrcode sample 35 | ``` 36 | 37 | - `Enable qrcode sample`: Enable QR code sample; 38 | 39 | Then let RT-Thread's package manager automatically update, or use the `pkgs --update` command to update the package to the BSP. 40 | 41 | ## 3. Use qrcode 42 | 43 | ### Generate QR code 44 | 45 | When using the qrcode software package, you must first define a structure to manage the QR code. 46 | 47 | ```c 48 | QRCode qrcode; 49 | ``` 50 | 51 | Then apply for dynamic memory according to the version number to save the generated QR code, 52 | 53 | ```c 54 | uint8_t *qrcodeBytes = (uint8_t *)rt_calloc(1, qrcode_getBufferSize(DEFAULT_QR_VERSION)); 55 | ``` 56 | 57 | Finally, use the QR code generation function to generate the QR code. 58 | 59 | ```c 60 | qrcode_initText(&qrcode, qrcodeBytes, DEFAULT_QR_VERSION, ECC_LOW, "HELLO WORLD"); 61 | ``` 62 | 63 | ### Print QR code 64 | 65 | The generated two-dimensional code is dot matrix data, 8 dots constitute a Byte. The following codes can be used to display the QR code 66 | 67 | ```c 68 | for (uint8_t y = 0; y 33 | [*] qrcode: A simple library for generating QR codes in C 34 | [*] Enable qrcode sample 35 | ``` 36 | 37 | - `Enable qrcode sample`: 开启二维码例程; 38 | 39 | 然后让 RT-Thread 的包管理器自动更新,或者使用 `pkgs --update` 命令更新包到 BSP 中。 40 | 41 | ## 3、使用 qrcode 42 | 43 | ### 生成二维码 44 | 45 | 在使用 qrcode 软件包时首先要定义一个结构体来管理二维码, 46 | 47 | ```c 48 | QRCode qrcode; 49 | ``` 50 | 51 | 然后根据要版本号来申请动态内存,用来保存生成的二维码, 52 | 53 | ```c 54 | uint8_t *qrcodeBytes = (uint8_t *)rt_calloc(1, qrcode_getBufferSize(DEFAULT_QR_VERSION)); 55 | ``` 56 | 57 | 最后使用二维码生成函数生成二维码。 58 | 59 | ```c 60 | qrcode_initText(&qrcode, qrcodeBytes, DEFAULT_QR_VERSION, ECC_LOW, "HELLO WORLD"); 61 | ``` 62 | 63 | ### 打印二维码 64 | 65 | 生成的二维码是点阵数据,8个点构成一个 Byte。显示二维码可以使用以下代码 66 | 67 | ```c 68 | for (uint8_t y = 0; y < qrcode.size; y++) { 69 | for (uint8_t x = 0; x < qrcode.size; x++) { 70 | if (qrcode_getModule(&qrcode, x, y)) { 71 | rt_kprintf("**"); 72 | } else { 73 | rt_kprintf(" "); 74 | } 75 | } 76 | Serial.print("\n"); 77 | } 78 | ``` 79 | 80 | ## 4、示例演示 81 | 82 | 本示例需要在 qrcode 软件包中开启二维码例程。 83 | 84 | 在 MSH 中输入命令 qrcode RT-Thread,可以在串口助手上打印出一个二维码来,利用手机扫码软件扫描后可以看到结果为 RT-Thread。 85 | 86 | ![qrcode](figures/qrcode.png) 87 | 88 | ### 其他例程 89 | 90 | https://github.com/RT-Thread/rt-thread/tree/master/bsp/stm32/stm32l475-atk-pandora/board/ports/lcd 91 | 92 | https://github.com/onelife/RTT-QRCode 93 | 94 | ## 5、注意事项 95 | 96 | - 在生成二维码时,使用的版本越高,需要申请的动态内存就越大。 97 | 98 | ## 6、联系方式 & 感谢 99 | 100 | * 维护:[qgyhd1234](https://github.com/qgyhd1234) [mysterywolf](https://github.com/mysterywolf) 101 | * 主页:https://github.com/RT-Thread-packages/qrcode 102 | 103 | -------------------------------------------------------------------------------- /README_offical.md: -------------------------------------------------------------------------------- 1 | QRCode 2 | ====== 3 | 4 | A simple library for generating [QR codes](https://en.wikipedia.org/wiki/QR_code) in C, 5 | optimized for processing and memory constrained systems. 6 | 7 | **Features:** 8 | 9 | - Stack-based (no heap necessary; but you can use heap if you want) 10 | - Low-memory foot print (relatively) 11 | - Compile-time stripping of unecessary logic and constants 12 | - MIT License; do with this as you please 13 | 14 | 15 | Installing 16 | ---------- 17 | 18 | To install this library, download and save it to your Arduino libraries directory. 19 | 20 | Rename the directory to QRCode (if downloaded from GitHub, the filename may be 21 | qrcode-master; library names may not contain the hyphen, so it must be renamed) 22 | 23 | 24 | API 25 | --- 26 | 27 | **Generate a QR Code** 28 | 29 | ```c 30 | // The structure to manage the QR code 31 | QRCode qrcode; 32 | 33 | // Allocate a chunk of memory to store the QR code 34 | uint8_t qrcodeBytes[qrcode_getBufferSize(3)]; 35 | 36 | qrcode_initText(&qrcode, qrcodeBytes, 3, ECC_LOW, "HELLO WORLD"); 37 | ``` 38 | 39 | **Draw a QR Code** 40 | 41 | How a QR code is used will vary greatly from project to project. For example: 42 | 43 | - Display on an OLED screen (128x64 nicely supports 2 side-by-side version 3 QR codes) 44 | - Print as a bitmap on a thermal printer 45 | - Store as a BMP (or with a some extra work, possibly a PNG) on an SD card 46 | 47 | The following example prints a QR code to the Serial Monitor (it likely will 48 | not be scannable, but is just for demonstration purposes). 49 | 50 | ```c 51 | for (uint8_t y = 0; y < qrcode.size; y++) { 52 | for (uint8_t x = 0; x < qrcode.size; x++) { 53 | if (qrcode_getModule(&qrcode, x, y)) { 54 | Serial.print("**"); 55 | } else { 56 | Serial.print(" "); 57 | } 58 | } 59 | Serial.print("\n"); 60 | } 61 | ``` 62 | 63 | 64 | What is Version, Error Correction and Mode? 65 | ------------------------------------------- 66 | 67 | A QR code is composed of many little squares, called **modules**, which represent 68 | encoded data, with additional error correction (allowing partially damaged QR 69 | codes to still be read). 70 | 71 | The **version** of a QR code is a number between 1 and 40 (inclusive), which indicates 72 | the size of the QR code. The width and height of a QR code are always equal (it is 73 | square) and are equal to `4 * version + 17`. 74 | 75 | The level of **error correction** is a number between 0 and 3 (inclusive), or can be 76 | one of the symbolic names ECC_LOW, ECC_MEDIUM, ECC_QUARTILE and ECC_HIGH. Higher 77 | levels of error correction sacrifice data capacity, but allow a larger portion of 78 | the QR code to be damaged or unreadable. 79 | 80 | The **mode** of a QR code is determined by the data being encoded. Each mode is encoded 81 | internally using a compact representation, so lower modes can contain more data. 82 | 83 | - **NUMERIC:** numbers (`0-9`) 84 | - **ALPHANUMERIC:** uppercase letters (`A-Z`), numbers (`0-9`), the space (` `), dollar sign (`$`), percent sign (`%`), asterisk (`*`), plus (`+`), minus (`-`), decimal point (`.`), slash (`/`) and colon (`:`). 85 | - **BYTE:** any character 86 | 87 | 88 | Data Capacities 89 | --------------- 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 |
VersionSizeError CorrectionMode
NumericAlphanumericByte
121 x 21LOW412517
MEDIUM342014
QUARTILE271611
HIGH17107
225 x 25LOW774732
MEDIUM633826
QUARTILE482920
HIGH342014
329 x 29LOW1277753
MEDIUM1016142
QUARTILE774732
HIGH583524
433 x 33LOW18711478
MEDIUM1499062
QUARTILE1116746
HIGH825034
537 x 37LOW255154106
MEDIUM20212284
QUARTILE1448760
HIGH1066444
641 x 41LOW322195134
MEDIUM255154106
QUARTILE17810874
HIGH1398458
745 x 45LOW370224154
MEDIUM293178122
QUARTILE20712586
HIGH1549364
849 x 49LOW461279192
MEDIUM365221152
QUARTILE259157108
HIGH20212284
953 x 53LOW552335230
MEDIUM432262180
QUARTILE312189130
HIGH23514398
1057 x 57LOW652395271
MEDIUM513311213
QUARTILE364221151
HIGH288174119
1161 x 61LOW772468321
MEDIUM604366251
QUARTILE427259177
HIGH331200137
1265 x 65LOW883535367
MEDIUM691419287
QUARTILE489296203
HIGH374227155
1369 x 69LOW1022619425
MEDIUM796483331
QUARTILE580352241
HIGH427259177
1473 x 73LOW1101667458
MEDIUM871528362
QUARTILE621376258
HIGH468283194
1577 x 77LOW1250758520
MEDIUM991600412
QUARTILE703426292
HIGH530321220
1681 x 81LOW1408854586
MEDIUM1082656450
QUARTILE775470322
HIGH602365250
1785 x 85LOW1548938644
MEDIUM1212734504
QUARTILE876531364
HIGH674408280
1889 x 89LOW17251046718
MEDIUM1346816560
QUARTILE948574394
HIGH746452310
1993 x 93LOW19031153792
MEDIUM1500909624
QUARTILE1063644442
HIGH813493338
2097 x 97LOW20611249858
MEDIUM1600970666
QUARTILE1159702482
HIGH919557382
21101 x 101LOW22321352929
MEDIUM17081035711
QUARTILE1224742509
HIGH969587403
22105 x 105LOW240914601003
MEDIUM18721134779
QUARTILE1358823565
HIGH1056640439
23109 x 109LOW262015881091
MEDIUM20591248857
QUARTILE1468890611
HIGH1108672461
24113 x 113LOW281217041171
MEDIUM21881326911
QUARTILE1588963661
HIGH1228744511
25117 x 117LOW305718531273
MEDIUM23951451997
QUARTILE17181041715
HIGH1286779535
26121 x 121LOW328319901367
MEDIUM254415421059
QUARTILE18041094751
HIGH1425864593
27125 x 125LOW351721321465
MEDIUM270116371125
QUARTILE19331172805
HIGH1501910625
28129 x 129LOW366922231528
MEDIUM285717321190
QUARTILE20851263868
HIGH1581958658
29133 x 133LOW390923691628
MEDIUM303518391264
QUARTILE21811322908
HIGH16771016698
30137 x 137LOW415825201732
MEDIUM328919941370
QUARTILE23581429982
HIGH17821080742
31141 x 141LOW441726771840
MEDIUM348621131452
QUARTILE247314991030
HIGH18971150790
32145 x 145LOW468628401952
MEDIUM369322381538
QUARTILE267016181112
HIGH20221226842
33149 x 149LOW496530092068
MEDIUM390923691628
QUARTILE280517001168
HIGH21571307898
34153 x 153LOW525331832188
MEDIUM413425061722
QUARTILE294917871228
HIGH23011394958
35157 x 157LOW552933512303
MEDIUM434326321809
QUARTILE308118671283
HIGH23611431983
36161 x 161LOW583635372431
MEDIUM458827801911
QUARTILE324419661351
HIGH252415301051
37165 x 165LOW615337292563
MEDIUM477528941989
QUARTILE341720711423
HIGH262515911093
38169 x 169LOW647939272699
MEDIUM503930542099
QUARTILE359921811499
HIGH273516581139
39173 x 173LOW674340872809
MEDIUM531332202213
QUARTILE379122981579
HIGH292717741219
40177 x 177LOW708942962953
MEDIUM559633912331
QUARTILE399324201663
HIGH305718521273
664 | 665 | 666 | Special Thanks 667 | -------------- 668 | 669 | A HUGE thank you to [Project Nayuki](https://www.nayuki.io/) for the 670 | [QR code C++ library](https://github.com/nayuki/QR-Code-generator/tree/master/cpp) 671 | which was critical in development of this library. 672 | 673 | 674 | License 675 | ------- 676 | 677 | MIT License. 678 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | 3 | cwd = GetCurrentDir() 4 | CPPPATH = [cwd] 5 | 6 | src = ['qrcode.c'] 7 | 8 | if GetDepend(['PKG_QRCODE_SAMPLE']): 9 | src += ['qrcode_sample.c'] 10 | 11 | group = DefineGroup('qrcode', src, depend = ['PKG_USING_QRCODE'], CPPPATH = CPPPATH) 12 | 13 | Return('group') 14 | -------------------------------------------------------------------------------- /examples/QRCode/QRCode.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * QRCode 3 | * 4 | * A quick example of generating a QR code. 5 | * 6 | * This prints the QR code to the serial monitor as solid blocks. Each module 7 | * is two characters wide, since the monospace font used in the serial monitor 8 | * is approximately twice as tall as wide. 9 | * 10 | */ 11 | 12 | #include "qrcode.h" 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | 17 | // Start time 18 | uint32_t dt = millis(); 19 | 20 | // Create the QR code 21 | QRCode qrcode; 22 | uint8_t qrcodeData[qrcode_getBufferSize(3)]; 23 | qrcode_initText(&qrcode, qrcodeData, 3, 0, "HELLO WORLD"); 24 | 25 | // Delta time 26 | dt = millis() - dt; 27 | Serial.print("QR Code Generation Time: "); 28 | Serial.print(dt); 29 | Serial.print("\n"); 30 | 31 | // Top quiet zone 32 | Serial.print("\n\n\n\n"); 33 | 34 | for (uint8_t y = 0; y < qrcode.size; y++) { 35 | 36 | // Left quiet zone 37 | Serial.print(" "); 38 | 39 | // Each horizontal module 40 | for (uint8_t x = 0; x < qrcode.size; x++) { 41 | 42 | // Print each module (UTF-8 \u2588 is a solid block) 43 | Serial.print(qrcode_getModule(&qrcode, x, y) ? "\u2588\u2588": " "); 44 | 45 | } 46 | 47 | Serial.print("\n"); 48 | } 49 | 50 | // Bottom quiet zone 51 | Serial.print("\n\n\n\n"); 52 | } 53 | 54 | void loop() { 55 | 56 | } 57 | -------------------------------------------------------------------------------- /examples/QRCode/QRCode_halfsize.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * QRCode 3 | * 4 | * A quick example of generating a QR code. 5 | * 6 | * This prints the QR code to the serial monitor as solid blocks. Each character 7 | * contains two blocks, since the monospace font used in the serial monitor 8 | * is approximately twice as tall as wide. 9 | * 10 | */ 11 | 12 | #include "qrcode.h" 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | 17 | // Start time 18 | uint32_t dt = millis(); 19 | 20 | // Create the QR code 21 | QRCode qrcode; 22 | uint8_t qrcodeData[qrcode_getBufferSize(3)]; 23 | qrcode_initText(&qrcode, qrcodeData, 3, 0, "HELLO WORLD"); 24 | 25 | // Delta time 26 | dt = millis() - dt; 27 | Serial.print("QR Code Generation Time: "); 28 | Serial.print(dt); 29 | Serial.print("\n"); 30 | 31 | // Top quiet zone 32 | Serial.print("\n\n\n\n"); 33 | 34 | for (uint8_t y = 0; y < qrcode.size; y += 2) { 35 | 36 | // Left quiet zone 37 | Serial.print(" "); 38 | 39 | // Each horizontal module 40 | for (uint8_t x = 0; x < qrcode.size; x++) { 41 | uint8_t block = qrcode_getModule(&qrcode, x, y) << 1 | qrcode_getModule(&qrcode, x, y + 1); 42 | 43 | switch (block) 44 | { 45 | case 0b00: 46 | Serial.print(" "); 47 | break; 48 | 49 | case 0b01: 50 | Serial.print("\u2584"); // \u2584 lower block 51 | break; 52 | 53 | case 0b10: 54 | Serial.print("\u2580"); // \u2580 upper block 55 | break; 56 | 57 | case 0b11: 58 | Serial.print("\u2588"); // \u2588 both blocks 59 | break; 60 | } 61 | } 62 | 63 | Serial.print("\n"); 64 | } 65 | 66 | // Bottom quiet zone 67 | Serial.print("\n\n\n\n"); 68 | } 69 | 70 | void loop() { 71 | 72 | } 73 | -------------------------------------------------------------------------------- /figures/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/qrcode/b31ef235ec04cf437cdcb040d75b0f3f7f809e27/figures/qrcode.png -------------------------------------------------------------------------------- /qrcode.c: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was 27 | * heavily inspired and compared against. 28 | * 29 | * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp 30 | */ 31 | 32 | /** 33 | * This package is RT-Thread adaptation to the QRCode library. 34 | * 35 | * See: https://github.com/RT-Thread-packages/qrcode 36 | */ 37 | 38 | #include "qrcode.h" 39 | #include 40 | 41 | #define DBG_ENABLE 42 | #define DBG_SECTION_NAME "qrcode" 43 | #define DBG_LEVEL DBG_LOG 44 | #include 45 | 46 | #include 47 | #include 48 | 49 | #if LOCK_VERSION == 0 50 | 51 | static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = { 52 | // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level 53 | { 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium 54 | { 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low 55 | { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High 56 | { 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile 57 | }; 58 | 59 | static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = { 60 | // Version: (note that index 0 is for padding, and is set to an illegal value) 61 | // 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level 62 | { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium 63 | { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low 64 | { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High 65 | { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile 66 | }; 67 | 68 | static const uint16_t NUM_RAW_DATA_MODULES[40] = { 69 | // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 70 | 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523, 71 | // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 72 | 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587, 73 | // 32, 33, 34, 35, 36, 37, 38, 39, 40 74 | 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648 75 | }; 76 | 77 | // @TODO: Put other LOCK_VERSIONS here 78 | #elif LOCK_VERSION == 3 79 | 80 | static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = { 81 | 26, 15, 44, 36 82 | }; 83 | 84 | static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = { 85 | 1, 1, 2, 2 86 | }; 87 | 88 | static const uint16_t NUM_RAW_DATA_MODULES = 567; 89 | 90 | #else 91 | 92 | #error Unsupported LOCK_VERSION (add it...) 93 | 94 | #endif 95 | 96 | static int max(int a, int b) { 97 | if (a > b) { return a; } 98 | return b; 99 | } 100 | 101 | static int8_t getAlphanumeric(char c) { 102 | 103 | if (c >= '0' && c <= '9') { return (c - '0'); } 104 | if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); } 105 | 106 | switch (c) { 107 | case ' ': return 36; 108 | case '$': return 37; 109 | case '%': return 38; 110 | case '*': return 39; 111 | case '+': return 40; 112 | case '-': return 41; 113 | case '.': return 42; 114 | case '/': return 43; 115 | case ':': return 44; 116 | } 117 | 118 | return -1; 119 | } 120 | 121 | static bool isAlphanumeric(const char *text, uint16_t length) { 122 | while (length != 0) { 123 | if (getAlphanumeric(text[--length]) == -1) { return false; } 124 | } 125 | return true; 126 | } 127 | 128 | static bool isNumeric(const char *text, uint16_t length) { 129 | while (length != 0) { 130 | char c = text[--length]; 131 | if (c < '0' || c > '9') { return false; } 132 | } 133 | return true; 134 | } 135 | 136 | // We store the following tightly packed (less 8) in modeInfo 137 | // <=9 <=26 <= 40 138 | // NUMERIC ( 10, 12, 14); 139 | // ALPHANUMERIC ( 9, 11, 13); 140 | // BYTE ( 8, 16, 16); 141 | static char getModeBits(uint8_t version, uint8_t mode) { 142 | // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits 143 | // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2)) 144 | unsigned int modeInfo = 0x7bbb80a; 145 | 146 | #if LOCK_VERSION == 0 || LOCK_VERSION > 9 147 | if (version > 9) { modeInfo >>= 9; } 148 | #endif 149 | 150 | #if LOCK_VERSION == 0 || LOCK_VERSION > 26 151 | if (version > 26) { modeInfo >>= 9; } 152 | #endif 153 | 154 | char result = 8 + ((modeInfo >> (3 * mode)) & 0x07); 155 | if (result == 15) { result = 16; } 156 | 157 | return result; 158 | } 159 | 160 | typedef struct BitBucket { 161 | uint32_t bitOffsetOrWidth; 162 | uint16_t capacityBytes; 163 | uint8_t *data; 164 | } BitBucket; 165 | 166 | static uint16_t bb_getGridSizeBytes(uint8_t size) { 167 | return (((size * size) + 7) / 8); 168 | } 169 | 170 | static uint16_t bb_getBufferSizeBytes(uint32_t bits) { 171 | return ((bits + 7) / 8); 172 | } 173 | 174 | static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) { 175 | bitBuffer->bitOffsetOrWidth = 0; 176 | bitBuffer->capacityBytes = capacityBytes; 177 | bitBuffer->data = data; 178 | 179 | memset(data, 0, bitBuffer->capacityBytes); 180 | } 181 | 182 | static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) { 183 | bitGrid->bitOffsetOrWidth = size; 184 | bitGrid->capacityBytes = bb_getGridSizeBytes(size); 185 | bitGrid->data = data; 186 | 187 | memset(data, 0, bitGrid->capacityBytes); 188 | } 189 | 190 | static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) { 191 | uint32_t offset = bitBuffer->bitOffsetOrWidth; 192 | for (int8_t i = length - 1; i >= 0; i--, offset++) { 193 | if (bitBuffer->capacityBytes > offset >> 3) { 194 | bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); 195 | } 196 | } 197 | bitBuffer->bitOffsetOrWidth = offset; 198 | } 199 | 200 | static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) { 201 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 202 | uint8_t mask = 1 << (7 - (offset & 0x07)); 203 | if (on) { 204 | bitGrid->data[offset >> 3] |= mask; 205 | } else { 206 | bitGrid->data[offset >> 3] &= ~mask; 207 | } 208 | } 209 | 210 | static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) { 211 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 212 | uint8_t mask = 1 << (7 - (offset & 0x07)); 213 | bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0); 214 | if (on ^ invert) { 215 | bitGrid->data[offset >> 3] |= mask; 216 | } else { 217 | bitGrid->data[offset >> 3] &= ~mask; 218 | } 219 | } 220 | 221 | static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) { 222 | uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; 223 | return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; 224 | } 225 | 226 | // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical 227 | // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. 228 | // This means it is possible to apply a mask, undo it, and try another mask. Note that a final 229 | // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). 230 | static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) { 231 | uint8_t size = modules->bitOffsetOrWidth; 232 | 233 | for (uint8_t y = 0; y < size; y++) { 234 | for (uint8_t x = 0; x < size; x++) { 235 | if (bb_getBit(isFunction, x, y)) { continue; } 236 | 237 | bool invert = 0; 238 | switch (mask) { 239 | case 0: invert = (x + y) % 2 == 0; break; 240 | case 1: invert = y % 2 == 0; break; 241 | case 2: invert = x % 3 == 0; break; 242 | case 3: invert = (x + y) % 3 == 0; break; 243 | case 4: invert = (x / 3 + y / 2) % 2 == 0; break; 244 | case 5: invert = x * y % 2 + x * y % 3 == 0; break; 245 | case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; 246 | case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; 247 | } 248 | bb_invertBit(modules, x, y, invert); 249 | } 250 | } 251 | } 252 | 253 | static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) { 254 | bb_setBit(modules, x, y, on); 255 | bb_setBit(isFunction, x, y, true); 256 | } 257 | 258 | // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). 259 | static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { 260 | uint8_t size = modules->bitOffsetOrWidth; 261 | 262 | for (int8_t i = -4; i <= 4; i++) { 263 | for (int8_t j = -4; j <= 4; j++) { 264 | uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm 265 | int16_t xx = x + j, yy = y + i; 266 | if (0 <= xx && xx < size && 0 <= yy && yy < size) { 267 | setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4); 268 | } 269 | } 270 | } 271 | } 272 | 273 | // Draws a 5*5 alignment pattern, with the center module at (x, y). 274 | static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { 275 | for (int8_t i = -2; i <= 2; i++) { 276 | for (int8_t j = -2; j <= 2; j++) { 277 | setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1); 278 | } 279 | } 280 | } 281 | 282 | // Draws two copies of the format bits (with its own error correction code) 283 | // based on the given mask and this object's error correction level field. 284 | static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) { 285 | 286 | uint8_t size = modules->bitOffsetOrWidth; 287 | 288 | // Calculate error correction code and pack bits 289 | uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3 290 | uint32_t rem = data; 291 | for (int i = 0; i < 10; i++) { 292 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 293 | } 294 | 295 | data = data << 10 | rem; 296 | data ^= 0x5412; // uint15 297 | 298 | // Draw first copy 299 | for (uint8_t i = 0; i <= 5; i++) { 300 | setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0); 301 | } 302 | 303 | setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0); 304 | setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0); 305 | setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0); 306 | 307 | for (int8_t i = 9; i < 15; i++) { 308 | setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0); 309 | } 310 | 311 | // Draw second copy 312 | for (int8_t i = 0; i <= 7; i++) { 313 | setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0); 314 | } 315 | 316 | for (int8_t i = 8; i < 15; i++) { 317 | setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0); 318 | } 319 | 320 | setFunctionModule(modules, isFunction, 8, size - 8, true); 321 | } 322 | 323 | // Draws two copies of the version bits (with its own error correction code), 324 | // based on this object's version field (which only has an effect for 7 <= version <= 40). 325 | static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) { 326 | 327 | int8_t size = modules->bitOffsetOrWidth; 328 | 329 | #if LOCK_VERSION != 0 && LOCK_VERSION < 7 330 | return; 331 | 332 | #else 333 | if (version < 7) { return; } 334 | 335 | // Calculate error correction code and pack bits 336 | uint32_t rem = version; // version is uint6, in the range [7, 40] 337 | for (uint8_t i = 0; i < 12; i++) { 338 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 339 | } 340 | 341 | uint32_t data = version << 12 | rem; // uint18 342 | 343 | // Draw two copies 344 | for (uint8_t i = 0; i < 18; i++) { 345 | bool bit = ((data >> i) & 1) != 0; 346 | uint8_t a = size - 11 + i % 3, b = i / 3; 347 | setFunctionModule(modules, isFunction, a, b, bit); 348 | setFunctionModule(modules, isFunction, b, a, bit); 349 | } 350 | 351 | #endif 352 | } 353 | 354 | static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) { 355 | 356 | uint8_t size = modules->bitOffsetOrWidth; 357 | 358 | // Draw the horizontal and vertical timing patterns 359 | for (uint8_t i = 0; i < size; i++) { 360 | setFunctionModule(modules, isFunction, 6, i, i % 2 == 0); 361 | setFunctionModule(modules, isFunction, i, 6, i % 2 == 0); 362 | } 363 | 364 | // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) 365 | drawFinderPattern(modules, isFunction, 3, 3); 366 | drawFinderPattern(modules, isFunction, size - 4, 3); 367 | drawFinderPattern(modules, isFunction, 3, size - 4); 368 | 369 | #if LOCK_VERSION == 0 || LOCK_VERSION > 1 370 | 371 | if (version > 1) { 372 | 373 | // Draw the numerous alignment patterns 374 | 375 | uint8_t alignCount = version / 7 + 2; 376 | uint8_t step; 377 | if (version != 32) { 378 | step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 379 | } else { // C-C-C-Combo breaker! 380 | step = 26; 381 | } 382 | 383 | uint8_t alignPositionIndex = alignCount - 1; 384 | uint8_t *alignPosition = (uint8_t *)rt_calloc(1, alignCount); 385 | if (!alignPosition) { 386 | LOG_W("No Memory"); 387 | return; 388 | } 389 | 390 | alignPosition[0] = 6; 391 | 392 | uint8_t size = version * 4 + 17; 393 | for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) { 394 | alignPosition[alignPositionIndex--] = pos; 395 | } 396 | 397 | for (uint8_t i = 0; i < alignCount; i++) { 398 | for (uint8_t j = 0; j < alignCount; j++) { 399 | if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) { 400 | continue; // Skip the three finder corners 401 | } else { 402 | drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]); 403 | } 404 | } 405 | } 406 | rt_free(alignPosition); 407 | } 408 | 409 | #endif 410 | 411 | // Draw configuration data 412 | drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor 413 | drawVersion(modules, isFunction, version); 414 | } 415 | 416 | 417 | // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire 418 | // data area of this QR Code symbol. Function modules need to be marked off before this is called. 419 | static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) { 420 | 421 | uint32_t bitLength = codewords->bitOffsetOrWidth; 422 | uint8_t *data = codewords->data; 423 | 424 | uint8_t size = modules->bitOffsetOrWidth; 425 | 426 | // Bit index into the data 427 | uint32_t i = 0; 428 | 429 | // Do the funny zigzag scan 430 | for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair 431 | if (right == 6) { right = 5; } 432 | 433 | for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter 434 | for (int j = 0; j < 2; j++) { 435 | uint8_t x = right - j; // Actual x coordinate 436 | bool upwards = ((right & 2) == 0) ^ (x < 6); 437 | uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate 438 | if (!bb_getBit(isFunction, x, y) && i < bitLength) { 439 | bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0); 440 | i++; 441 | } 442 | // If there are any remainder bits (0 to 7), they are already 443 | // set to 0/false/white when the grid of modules was initialized 444 | } 445 | } 446 | } 447 | } 448 | 449 | #define PENALTY_N1 3 450 | #define PENALTY_N2 3 451 | #define PENALTY_N3 40 452 | #define PENALTY_N4 10 453 | 454 | // Calculates and returns the penalty score based on state of this QR Code's current modules. 455 | // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. 456 | // @TODO: This can be optimized by working with the bytes instead of bits. 457 | static uint32_t getPenaltyScore(BitBucket *modules) { 458 | uint32_t result = 0; 459 | 460 | uint8_t size = modules->bitOffsetOrWidth; 461 | 462 | // Adjacent modules in row having same color 463 | for (uint8_t y = 0; y < size; y++) { 464 | 465 | bool colorX = bb_getBit(modules, 0, y); 466 | for (uint8_t x = 1, runX = 1; x < size; x++) { 467 | bool cx = bb_getBit(modules, x, y); 468 | if (cx != colorX) { 469 | colorX = cx; 470 | runX = 1; 471 | 472 | } else { 473 | runX++; 474 | if (runX == 5) { 475 | result += PENALTY_N1; 476 | } else if (runX > 5) { 477 | result++; 478 | } 479 | } 480 | } 481 | } 482 | 483 | // Adjacent modules in column having same color 484 | for (uint8_t x = 0; x < size; x++) { 485 | bool colorY = bb_getBit(modules, x, 0); 486 | for (uint8_t y = 1, runY = 1; y < size; y++) { 487 | bool cy = bb_getBit(modules, x, y); 488 | if (cy != colorY) { 489 | colorY = cy; 490 | runY = 1; 491 | } else { 492 | runY++; 493 | if (runY == 5) { 494 | result += PENALTY_N1; 495 | } else if (runY > 5) { 496 | result++; 497 | } 498 | } 499 | } 500 | } 501 | 502 | uint16_t black = 0; 503 | for (uint8_t y = 0; y < size; y++) { 504 | uint16_t bitsRow = 0, bitsCol = 0; 505 | for (uint8_t x = 0; x < size; x++) { 506 | bool color = bb_getBit(modules, x, y); 507 | 508 | // 2*2 blocks of modules having same color 509 | if (x > 0 && y > 0) { 510 | bool colorUL = bb_getBit(modules, x - 1, y - 1); 511 | bool colorUR = bb_getBit(modules, x, y - 1); 512 | bool colorL = bb_getBit(modules, x - 1, y); 513 | if (color == colorUL && color == colorUR && color == colorL) { 514 | result += PENALTY_N2; 515 | } 516 | } 517 | 518 | // Finder-like pattern in rows and columns 519 | bitsRow = ((bitsRow << 1) & 0x7FF) | color; 520 | bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x); 521 | 522 | // Needs 11 bits accumulated 523 | if (x >= 10) { 524 | if (bitsRow == 0x05D || bitsRow == 0x5D0) { 525 | result += PENALTY_N3; 526 | } 527 | if (bitsCol == 0x05D || bitsCol == 0x5D0) { 528 | result += PENALTY_N3; 529 | } 530 | } 531 | 532 | // Balance of black and white modules 533 | if (color) { black++; } 534 | } 535 | } 536 | 537 | // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% 538 | uint16_t total = size * size; 539 | for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) { 540 | result += PENALTY_N4; 541 | } 542 | 543 | return result; 544 | } 545 | 546 | static uint8_t rs_multiply(uint8_t x, uint8_t y) { 547 | // Russian peasant multiplication 548 | // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication 549 | uint16_t z = 0; 550 | for (int8_t i = 7; i >= 0; i--) { 551 | z = (z << 1) ^ ((z >> 7) * 0x11D); 552 | z ^= ((y >> i) & 1) * x; 553 | } 554 | return z; 555 | } 556 | 557 | static void rs_init(uint8_t degree, uint8_t *coeff) { 558 | memset(coeff, 0, degree); 559 | coeff[degree - 1] = 1; 560 | 561 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), 562 | // drop the highest term, and store the rest of the coefficients in order of descending powers. 563 | // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). 564 | uint16_t root = 1; 565 | for (uint8_t i = 0; i < degree; i++) { 566 | // Multiply the current product by (x - r^i) 567 | for (uint8_t j = 0; j < degree; j++) { 568 | coeff[j] = rs_multiply(coeff[j], root); 569 | if (j + 1 < degree) { 570 | coeff[j] ^= coeff[j + 1]; 571 | } 572 | } 573 | root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) 574 | } 575 | } 576 | 577 | static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) { 578 | // Compute the remainder by performing polynomial division 579 | 580 | //for (uint8_t i = 0; i < degree; i++) { result[] = 0; } 581 | //memset(result, 0, degree); 582 | 583 | for (uint8_t i = 0; i < length; i++) { 584 | uint8_t factor = data[i] ^ result[0]; 585 | for (uint8_t j = 1; j < degree; j++) { 586 | result[(j - 1) * stride] = result[j * stride]; 587 | } 588 | result[(degree - 1) * stride] = 0; 589 | 590 | for (uint8_t j = 0; j < degree; j++) { 591 | result[j * stride] ^= rs_multiply(coeff[j], factor); 592 | } 593 | } 594 | } 595 | 596 | static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) { 597 | int8_t mode = MODE_BYTE; 598 | 599 | if (isNumeric((char*)text, length)) { 600 | mode = MODE_NUMERIC; 601 | bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4); 602 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC)); 603 | 604 | uint16_t accumData = 0; 605 | uint8_t accumCount = 0; 606 | for (uint16_t i = 0; i < length; i++) { 607 | accumData = accumData * 10 + ((char)(text[i]) - '0'); 608 | accumCount++; 609 | if (accumCount == 3) { 610 | bb_appendBits(dataCodewords, accumData, 10); 611 | accumData = 0; 612 | accumCount = 0; 613 | } 614 | } 615 | 616 | // 1 or 2 digits remaining 617 | if (accumCount > 0) { 618 | bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1); 619 | } 620 | 621 | } else if (isAlphanumeric((char*)text, length)) { 622 | mode = MODE_ALPHANUMERIC; 623 | bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4); 624 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC)); 625 | 626 | uint16_t accumData = 0; 627 | uint8_t accumCount = 0; 628 | for (uint16_t i = 0; i < length; i++) { 629 | accumData = accumData * 45 + getAlphanumeric((char)(text[i])); 630 | accumCount++; 631 | if (accumCount == 2) { 632 | bb_appendBits(dataCodewords, accumData, 11); 633 | accumData = 0; 634 | accumCount = 0; 635 | } 636 | } 637 | 638 | // 1 character remaining 639 | if (accumCount > 0) { 640 | bb_appendBits(dataCodewords, accumData, 6); 641 | } 642 | 643 | } else { 644 | bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4); 645 | bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE)); 646 | for (uint16_t i = 0; i < length; i++) { 647 | bb_appendBits(dataCodewords, (char)(text[i]), 8); 648 | } 649 | } 650 | 651 | //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode)); 652 | 653 | return mode; 654 | } 655 | 656 | static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) { 657 | 658 | // See: http://www.thonky.com/qr-code-tutorial/structure-final-message 659 | 660 | #if LOCK_VERSION == 0 661 | uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1]; 662 | uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1]; 663 | uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; 664 | #else 665 | uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc]; 666 | uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc]; 667 | uint16_t moduleCount = NUM_RAW_DATA_MODULES; 668 | #endif 669 | 670 | uint8_t blockEccLen = totalEcc / numBlocks; 671 | uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks; 672 | uint8_t shortBlockLen = moduleCount / 8 / numBlocks; 673 | 674 | uint8_t shortDataBlockLen = shortBlockLen - blockEccLen; 675 | 676 | uint8_t *result = (uint8_t *)rt_calloc(1, data->capacityBytes); 677 | uint8_t *coeff = (uint8_t *)rt_calloc(1, blockEccLen); 678 | if (!result || !coeff) { 679 | LOG_W("No Memory"); 680 | rt_free(result); 681 | rt_free(coeff); 682 | return; 683 | } 684 | rs_init(blockEccLen, coeff); 685 | 686 | uint16_t offset = 0; 687 | uint8_t *dataBytes = data->data; 688 | 689 | // Interleave all short blocks 690 | for (uint8_t i = 0; i < shortDataBlockLen; i++) { 691 | uint16_t index = i; 692 | uint8_t stride = shortDataBlockLen; 693 | for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { 694 | result[offset++] = dataBytes[index]; 695 | 696 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 697 | if (blockNum == numShortBlocks) { stride++; } 698 | #endif 699 | index += stride; 700 | } 701 | } 702 | 703 | // Version less than 5 only have short blocks 704 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 705 | { 706 | // Interleave long blocks 707 | uint16_t index = shortDataBlockLen * (numShortBlocks + 1); 708 | uint8_t stride = shortDataBlockLen; 709 | for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) { 710 | result[offset++] = dataBytes[index]; 711 | 712 | if (blockNum == 0) { stride++; } 713 | index += stride; 714 | } 715 | } 716 | #endif 717 | 718 | // Add all ecc blocks, interleaved 719 | uint8_t blockSize = shortDataBlockLen; 720 | for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { 721 | 722 | #if LOCK_VERSION == 0 || LOCK_VERSION >= 5 723 | if (blockNum == numShortBlocks) { blockSize++; } 724 | #endif 725 | rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks); 726 | dataBytes += blockSize; 727 | } 728 | 729 | memcpy(data->data, result, data->capacityBytes); 730 | data->bitOffsetOrWidth = moduleCount; 731 | 732 | rt_free(result); 733 | rt_free(coeff); 734 | } 735 | 736 | // We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits) 737 | // The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc) 738 | static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0); 739 | 740 | uint16_t qrcode_getBufferSize(uint8_t version) { 741 | return bb_getGridSizeBytes(4 * version + 17); 742 | } 743 | 744 | int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) { 745 | uint8_t size = version * 4 + 17; 746 | qrcode->version = version; 747 | qrcode->size = size; 748 | qrcode->ecc = ecc; 749 | qrcode->modules = modules; 750 | 751 | uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; 752 | 753 | #if LOCK_VERSION == 0 754 | uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; 755 | uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; 756 | #else 757 | version = LOCK_VERSION; 758 | uint16_t moduleCount = NUM_RAW_DATA_MODULES; 759 | uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; 760 | #endif 761 | 762 | struct BitBucket codewords; 763 | uint8_t *codewordBytes = (uint8_t *)rt_calloc(1, bb_getBufferSizeBytes(moduleCount)); 764 | if (!codewordBytes) { 765 | LOG_W("No Memory"); 766 | return -RT_ENOMEM; 767 | } 768 | bb_initBuffer(&codewords, codewordBytes, (int32_t)bb_getBufferSizeBytes(moduleCount)); 769 | 770 | // Place the data code words into the buffer 771 | int8_t mode = encodeDataCodewords(&codewords, data, length, version); 772 | 773 | if (mode < 0) { return -1; } 774 | qrcode->mode = mode; 775 | 776 | // Add terminator and pad up to a byte if applicable 777 | int32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth; 778 | if (padding < 0) { return -1; } 779 | if (padding > 4) { padding = 4; } 780 | bb_appendBits(&codewords, 0, padding); 781 | bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8); 782 | 783 | // Pad with alternate bytes until data capacity is reached 784 | for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) { 785 | bb_appendBits(&codewords, padByte, 8); 786 | } 787 | 788 | BitBucket modulesGrid; 789 | bb_initGrid(&modulesGrid, modules, size); 790 | 791 | BitBucket isFunctionGrid; 792 | uint8_t *isFunctionGridBytes = (uint8_t *)rt_calloc(1, bb_getGridSizeBytes(size)); 793 | if (!isFunctionGridBytes) { 794 | LOG_W("No Memory"); 795 | return -RT_ENOMEM; 796 | } 797 | bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size); 798 | 799 | drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits); 800 | performErrorCorrection(version, eccFormatBits, &codewords); 801 | drawCodewords(&modulesGrid, &isFunctionGrid, &codewords); 802 | 803 | // Find the best (lowest penalty) mask 804 | uint8_t mask = 0; 805 | int32_t minPenalty = INT32_MAX; 806 | for (uint8_t i = 0; i < 8; i++) { 807 | drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i); 808 | applyMask(&modulesGrid, &isFunctionGrid, i); 809 | int penalty = getPenaltyScore(&modulesGrid); 810 | if (penalty < minPenalty) { 811 | mask = i; 812 | minPenalty = penalty; 813 | } 814 | applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR 815 | } 816 | 817 | qrcode->mask = mask; 818 | 819 | // Overwrite old format bits 820 | drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask); 821 | 822 | // Apply the final choice of mask 823 | applyMask(&modulesGrid, &isFunctionGrid, mask); 824 | 825 | rt_free(isFunctionGridBytes); 826 | rt_free(codewordBytes); 827 | return 0; 828 | } 829 | 830 | int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) { 831 | return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data)); 832 | } 833 | 834 | bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) { 835 | if (x >= qrcode->size || y >= qrcode->size) { 836 | return false; 837 | } 838 | 839 | uint32_t offset = y * qrcode->size + x; 840 | return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; 841 | } 842 | -------------------------------------------------------------------------------- /qrcode.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Richard Moore 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was 27 | * heavily inspired and compared against. 28 | * 29 | * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp 30 | */ 31 | 32 | 33 | #ifndef __QRCODE_H_ 34 | #define __QRCODE_H_ 35 | 36 | #include 37 | #include 38 | 39 | // QR Code Format Encoding 40 | #define MODE_NUMERIC 0 41 | #define MODE_ALPHANUMERIC 1 42 | #define MODE_BYTE 2 43 | 44 | // Error Correction Code Levels 45 | #define ECC_LOW 0 46 | #define ECC_MEDIUM 1 47 | #define ECC_QUARTILE 2 48 | #define ECC_HIGH 3 49 | 50 | 51 | // If set to non-zero, this library can ONLY produce QR codes at that version 52 | // This saves a lot of dynamic memory, as the codeword tables are skipped 53 | #ifndef LOCK_VERSION 54 | #define LOCK_VERSION 0 55 | #endif 56 | 57 | 58 | typedef struct QRCode { 59 | uint8_t version; 60 | uint8_t size; 61 | uint8_t ecc; 62 | uint8_t mode; 63 | uint8_t mask; 64 | uint8_t *modules; 65 | } QRCode; 66 | 67 | 68 | #ifdef __cplusplus 69 | extern "C"{ 70 | #endif /* __cplusplus */ 71 | 72 | 73 | 74 | uint16_t qrcode_getBufferSize(uint8_t version); 75 | 76 | int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); 77 | int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); 78 | 79 | bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y); 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif /* __cplusplus */ 84 | 85 | 86 | #endif /* __QRCODE_H_ */ 87 | -------------------------------------------------------------------------------- /qrcode_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "qrcode.h" 3 | 4 | #if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) 5 | #include 6 | static void qrcode(uint8_t argc, char **argv) 7 | { 8 | #define DEFAULT_QR_VERSION 3 9 | #define DEFAULT_QR_STRING "HELLO WORLD" 10 | 11 | QRCode qrc; 12 | uint8_t x, y, *qrcodeBytes = (uint8_t *)rt_calloc(1, qrcode_getBufferSize(DEFAULT_QR_VERSION)); 13 | int8_t result; 14 | char *qrstr = DEFAULT_QR_STRING; 15 | 16 | if (qrcodeBytes) 17 | { 18 | if (argc > 1) 19 | { 20 | qrstr = argv[1]; 21 | } 22 | 23 | result = qrcode_initText(&qrc, qrcodeBytes, DEFAULT_QR_VERSION, ECC_LOW, qrstr); 24 | 25 | if (result >= 0) 26 | { 27 | rt_kprintf("\n"); 28 | for (y = 0; y < qrc.size; y++) 29 | { 30 | for (x = 0; x < qrc.size; x++) 31 | { 32 | if (qrcode_getModule(&qrc, x, y)) 33 | { 34 | rt_kprintf("\xdb\xdb"); 35 | } 36 | else 37 | { 38 | rt_kprintf(" "); 39 | } 40 | } 41 | rt_kprintf("\n"); 42 | } 43 | } 44 | else 45 | { 46 | rt_kprintf("QR CODE(%s) General FAILED(%d)\n", qrstr, result); 47 | } 48 | rt_free(qrcodeBytes); 49 | } 50 | else 51 | { 52 | rt_kprintf("Warning: no memory!\n"); 53 | } 54 | } 55 | MSH_CMD_EXPORT(qrcode, qrcode generator: qrcode [string]); 56 | #endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */ 57 | --------------------------------------------------------------------------------