Remove WiFi QrCode

This commit is contained in:
Phat Nguyen
2024-04-30 19:59:43 +07:00
parent 88808a2ad2
commit 7a182ebb12
12 changed files with 5 additions and 1874 deletions

View File

@ -1,6 +1,5 @@
#include "AgOledDisplay.h"
#include "Libraries/U8g2/src/U8g2lib.h"
#include "Libraries/QRCode/src/qrcode.h"
/** Cast U8G2 */
#define DISP() ((U8G2_SH1106_128X64_NONAME_F_HW_I2C *)(this->u8g2))
@ -286,25 +285,3 @@ void OledDisplay::showDashboard(const char *status) {
DISP()->drawStr(85, 63, strBuf);
} while (DISP()->nextPage());
}
void OledDisplay::showWiFiQrCode(String content, String label) {
QRCode qrcode;
int version = 6;
int x_start = (DISP()->getWidth() - (version * 4 + 17))/ 2;
uint8_t qrcodeData[qrcode_getBufferSize(version)];
qrcode_initText(&qrcode, qrcodeData, version, 0, content.c_str());
DISP()->firstPage();
do {
for (uint8_t y = 0; y < qrcode.size; y++) {
for (uint8_t x = 0; x < qrcode.size; x++) {
if (qrcode_getModule(&qrcode, x, y)) {
DISP()->drawPixel(x + x_start, y);
}
}
}
DISP()->setFont(u8g2_font_t0_16_tf);
x_start = (DISP()->getWidth() - DISP()->getStrWidth(label.c_str()))/2;
DISP()->drawStr(x_start, 60, label.c_str());
} while (DISP()->nextPage());
}

View File

@ -31,7 +31,6 @@ public:
const char *line4);
void showDashboard(void);
void showDashboard(const char *status);
void showWiFiQrCode(String content, String label);
};
#endif /** _AG_OLED_DISPLAY_H_ */

View File

@ -414,19 +414,12 @@ void StateMachine::displayHandle(AgStateMachineState state) {
switch (state) {
case AgStateMachineWiFiManagerMode:
case AgStateMachineWiFiManagerPortalActive: {
// if (wifiConnectCountDown >= 0) {
// String line1 = String(wifiConnectCountDown) + "s to connect";
// String line2 = "to WiFi hotspot:";
// String line3 = "\"airgradient-";
// String line4 = ag->deviceId() + "\"";
// disp.setText(line1, line2, line3, line4);
// wifiConnectCountDown--;
// }
if (wifiConnectCountDown >= 0) {
String qrContent = "WIFI:S:" + config.wifiSSID() +
";T:WPA;P:" + config.wifiPass() + ";;";
String label = "Scan me (" + String(wifiConnectCountDown) + String(")");
disp.showWiFiQrCode(qrContent, label);
String line1 = String(wifiConnectCountDown) + "s to connect";
String line2 = "to WiFi hotspot:";
String line3 = "\"airgradient-";
String line4 = ag->deviceId() + "\"";
disp.setText(line1, line2, line3, line4);
wifiConnectCountDown--;
}
break;

View File

@ -1 +0,0 @@
.DS_Store

View File

@ -1,26 +0,0 @@
The MIT License (MIT)
This library is written and maintained by Richard Moore.
Major parts were derived from Project Nayuki's library.
Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,677 +0,0 @@
QRCode
======
A simple library for generating [QR codes](https://en.wikipedia.org/wiki/QR_code) in C,
optimized for processing and memory constrained systems.
**Features:**
- Stack-based (no heap necessary; but you can use heap if you want)
- Low-memory foot print (relatively)
- Compile-time stripping of unecessary logic and constants
- MIT License; do with this as you please
Installing
----------
To install this library, download and save it to your Arduino libraries directory.
Rename the directory to QRCode (if downloaded from GitHub, the filename may be
qrcode-master; library names may not contain the hyphen, so it must be renamed)
API
---
**Generate a QR Code**
```c
// The structure to manage the QR code
QRCode qrcode;
// Allocate a chunk of memory to store the QR code
uint8_t qrcodeBytes[qrcode_getBufferSize()];
qrcode_initText(&qrcode, qrcodeBytes, 3, ECC_LOW, "HELLO WORLD");
```
**Draw a QR Code**
How a QR code is used will vary greatly from project to project. For example:
- Display on an OLED screen (128x64 nicely supports 2 side-by-side version 3 QR codes)
- Print as a bitmap on a thermal printer
- Store as a BMP (or with a some extra work, possibly a PNG) on an SD card
The following example prints a QR code to the Serial Monitor (it likely will
not be scannable, but is just for demonstration purposes).
```c
for (uint8 y = 0; y < qrcode.size; y++) {
for (uint8 x = 0; x < qrcode.size; x++) {
if (qrcode_getModule(&qrcode, x, y) {
Serial.print("**");
} else {
Serial.print(" ");
}
}
Serial.print("\n");
}
```
What is Version, Error Correction and Mode?
-------------------------------------------
A QR code is composed of many little squares, called **modules**, which represent
encoded data, with additional error correction (allowing partially damaged QR
codes to still be read).
The **version** of a QR code is a number between 1 and 40 (inclusive), which indicates
the size of the QR code. The width and height of a QR code are always equal (it is
square) and are equal to `4 * version + 17`.
The level of **error correction** is a number between 0 and 3 (inclusive), or can be
one of the symbolic names ECC_LOW, ECC_MEDIUM, ECC_QUARTILE and ECC_HIGH. Higher
levels of error correction sacrifice data capacity, but allow a larger portion of
the QR code to be damaged or unreadable.
The **mode** of a QR code is determined by the data being encoded. Each mode is encoded
internally using a compact representation, so lower modes can contain more data.
- **NUMERIC:** numbers (`0-9`)
- **ALPHANUMERIC:** uppercase letters (`A-Z`), numbers (`0-9`), the space (` `), dollar sign (`$`), percent sign (`%`), asterisk (`*`), plus (`+`), minus (`-`), decimal point (`.`), slash (`/`) and colon (`:`).
- **BYTE:** any character
Data Capacities
---------------
<table>
<tr>
<th rowspan="2">Version</th>
<th rowspan="2">Size</th>
<th rowspan="2">Error Correction</th>
<th colspan="3">Mode</th>
</tr>
<tr>
<th>Numeric</th>
<th>Alphanumeric</th>
<th>Byte</th>
</tr>
<tr>
<td rowspan="4">1</td>
<td rowspan="4">21 x 21</td>
<td>LOW</td><td>41</td><td>25</td><td>17</td>
</tr>
<tr>
<td>MEDIUM</td><td>34</td><td>20</td><td>14</td>
</tr>
<tr>
<td>QUARTILE</td><td>27</td><td>16</td><td>11</td>
</tr>
<tr>
<td>HIGH</td><td>17</td><td>10</td><td>7</td>
</tr>
<tr>
<td rowspan="4">2</td>
<td rowspan="4">25 x 25</td>
<td>LOW</td><td>77</td><td>47</td><td>32</td>
</tr>
<tr>
<td>MEDIUM</td><td>63</td><td>38</td><td>26</td>
</tr>
<tr>
<td>QUARTILE</td><td>48</td><td>29</td><td>20</td>
</tr>
<tr>
<td>HIGH</td><td>34</td><td>20</td><td>14</td>
</tr>
<tr>
<td rowspan="4">3</td>
<td rowspan="4">29 x 29</td>
<td>LOW</td><td>127</td><td>77</td><td>53</td>
</tr>
<tr>
<td>MEDIUM</td><td>101</td><td>61</td><td>42</td>
</tr>
<tr>
<td>QUARTILE</td><td>77</td><td>47</td><td>32</td>
</tr>
<tr>
<td>HIGH</td><td>58</td><td>35</td><td>24</td>
</tr>
<tr>
<td rowspan="4">4</td>
<td rowspan="4">33 x 33</td>
<td>LOW</td><td>187</td><td>114</td><td>78</td>
</tr>
<tr>
<td>MEDIUM</td><td>149</td><td>90</td><td>62</td>
</tr>
<tr>
<td>QUARTILE</td><td>111</td><td>67</td><td>46</td>
</tr>
<tr>
<td>HIGH</td><td>82</td><td>50</td><td>34</td>
</tr>
<tr>
<td rowspan="4">5</td>
<td rowspan="4">37 x 37</td>
<td>LOW</td><td>255</td><td>154</td><td>106</td>
</tr>
<tr>
<td>MEDIUM</td><td>202</td><td>122</td><td>84</td>
</tr>
<tr>
<td>QUARTILE</td><td>144</td><td>87</td><td>60</td>
</tr>
<tr>
<td>HIGH</td><td>106</td><td>64</td><td>44</td>
</tr>
<tr>
<td rowspan="4">6</td>
<td rowspan="4">41 x 41</td>
<td>LOW</td><td>322</td><td>195</td><td>134</td>
</tr>
<tr>
<td>MEDIUM</td><td>255</td><td>154</td><td>106</td>
</tr>
<tr>
<td>QUARTILE</td><td>178</td><td>108</td><td>74</td>
</tr>
<tr>
<td>HIGH</td><td>139</td><td>84</td><td>58</td>
</tr>
<tr>
<td rowspan="4">7</td>
<td rowspan="4">45 x 45</td>
<td>LOW</td><td>370</td><td>224</td><td>154</td>
</tr>
<tr>
<td>MEDIUM</td><td>293</td><td>178</td><td>122</td>
</tr>
<tr>
<td>QUARTILE</td><td>207</td><td>125</td><td>86</td>
</tr>
<tr>
<td>HIGH</td><td>154</td><td>93</td><td>64</td>
</tr>
<tr>
<td rowspan="4">8</td>
<td rowspan="4">49 x 49</td>
<td>LOW</td><td>461</td><td>279</td><td>192</td>
</tr>
<tr>
<td>MEDIUM</td><td>365</td><td>221</td><td>152</td>
</tr>
<tr>
<td>QUARTILE</td><td>259</td><td>157</td><td>108</td>
</tr>
<tr>
<td>HIGH</td><td>202</td><td>122</td><td>84</td>
</tr>
<tr>
<td rowspan="4">9</td>
<td rowspan="4">53 x 53</td>
<td>LOW</td><td>552</td><td>335</td><td>230</td>
</tr>
<tr>
<td>MEDIUM</td><td>432</td><td>262</td><td>180</td>
</tr>
<tr>
<td>QUARTILE</td><td>312</td><td>189</td><td>130</td>
</tr>
<tr>
<td>HIGH</td><td>235</td><td>143</td><td>98</td>
</tr>
<tr>
<td rowspan="4">10</td>
<td rowspan="4">57 x 57</td>
<td>LOW</td><td>652</td><td>395</td><td>271</td>
</tr>
<tr>
<td>MEDIUM</td><td>513</td><td>311</td><td>213</td>
</tr>
<tr>
<td>QUARTILE</td><td>364</td><td>221</td><td>151</td>
</tr>
<tr>
<td>HIGH</td><td>288</td><td>174</td><td>119</td>
</tr>
<tr>
<td rowspan="4">11</td>
<td rowspan="4">61 x 61</td>
<td>LOW</td><td>772</td><td>468</td><td>321</td>
</tr>
<tr>
<td>MEDIUM</td><td>604</td><td>366</td><td>251</td>
</tr>
<tr>
<td>QUARTILE</td><td>427</td><td>259</td><td>177</td>
</tr>
<tr>
<td>HIGH</td><td>331</td><td>200</td><td>137</td>
</tr>
<tr>
<td rowspan="4">12</td>
<td rowspan="4">65 x 65</td>
<td>LOW</td><td>883</td><td>535</td><td>367</td>
</tr>
<tr>
<td>MEDIUM</td><td>691</td><td>419</td><td>287</td>
</tr>
<tr>
<td>QUARTILE</td><td>489</td><td>296</td><td>203</td>
</tr>
<tr>
<td>HIGH</td><td>374</td><td>227</td><td>155</td>
</tr>
<tr>
<td rowspan="4">13</td>
<td rowspan="4">69 x 69</td>
<td>LOW</td><td>1022</td><td>619</td><td>425</td>
</tr>
<tr>
<td>MEDIUM</td><td>796</td><td>483</td><td>331</td>
</tr>
<tr>
<td>QUARTILE</td><td>580</td><td>352</td><td>241</td>
</tr>
<tr>
<td>HIGH</td><td>427</td><td>259</td><td>177</td>
</tr>
<tr>
<td rowspan="4">14</td>
<td rowspan="4">73 x 73</td>
<td>LOW</td><td>1101</td><td>667</td><td>458</td>
</tr>
<tr>
<td>MEDIUM</td><td>871</td><td>528</td><td>362</td>
</tr>
<tr>
<td>QUARTILE</td><td>621</td><td>376</td><td>258</td>
</tr>
<tr>
<td>HIGH</td><td>468</td><td>283</td><td>194</td>
</tr>
<tr>
<td rowspan="4">15</td>
<td rowspan="4">77 x 77</td>
<td>LOW</td><td>1250</td><td>758</td><td>520</td>
</tr>
<tr>
<td>MEDIUM</td><td>991</td><td>600</td><td>412</td>
</tr>
<tr>
<td>QUARTILE</td><td>703</td><td>426</td><td>292</td>
</tr>
<tr>
<td>HIGH</td><td>530</td><td>321</td><td>220</td>
</tr>
<tr>
<td rowspan="4">16</td>
<td rowspan="4">81 x 81</td>
<td>LOW</td><td>1408</td><td>854</td><td>586</td>
</tr>
<tr>
<td>MEDIUM</td><td>1082</td><td>656</td><td>450</td>
</tr>
<tr>
<td>QUARTILE</td><td>775</td><td>470</td><td>322</td>
</tr>
<tr>
<td>HIGH</td><td>602</td><td>365</td><td>250</td>
</tr>
<tr>
<td rowspan="4">17</td>
<td rowspan="4">85 x 85</td>
<td>LOW</td><td>1548</td><td>938</td><td>644</td>
</tr>
<tr>
<td>MEDIUM</td><td>1212</td><td>734</td><td>504</td>
</tr>
<tr>
<td>QUARTILE</td><td>876</td><td>531</td><td>364</td>
</tr>
<tr>
<td>HIGH</td><td>674</td><td>408</td><td>280</td>
</tr>
<tr>
<td rowspan="4">18</td>
<td rowspan="4">89 x 89</td>
<td>LOW</td><td>1725</td><td>1046</td><td>718</td>
</tr>
<tr>
<td>MEDIUM</td><td>1346</td><td>816</td><td>560</td>
</tr>
<tr>
<td>QUARTILE</td><td>948</td><td>574</td><td>394</td>
</tr>
<tr>
<td>HIGH</td><td>746</td><td>452</td><td>310</td>
</tr>
<tr>
<td rowspan="4">19</td>
<td rowspan="4">93 x 93</td>
<td>LOW</td><td>1903</td><td>1153</td><td>792</td>
</tr>
<tr>
<td>MEDIUM</td><td>1500</td><td>909</td><td>624</td>
</tr>
<tr>
<td>QUARTILE</td><td>1063</td><td>644</td><td>442</td>
</tr>
<tr>
<td>HIGH</td><td>813</td><td>493</td><td>338</td>
</tr>
<tr>
<td rowspan="4">20</td>
<td rowspan="4">97 x 97</td>
<td>LOW</td><td>2061</td><td>1249</td><td>858</td>
</tr>
<tr>
<td>MEDIUM</td><td>1600</td><td>970</td><td>666</td>
</tr>
<tr>
<td>QUARTILE</td><td>1159</td><td>702</td><td>482</td>
</tr>
<tr>
<td>HIGH</td><td>919</td><td>557</td><td>382</td>
</tr>
<tr>
<td rowspan="4">21</td>
<td rowspan="4">101 x 101</td>
<td>LOW</td><td>2232</td><td>1352</td><td>929</td>
</tr>
<tr>
<td>MEDIUM</td><td>1708</td><td>1035</td><td>711</td>
</tr>
<tr>
<td>QUARTILE</td><td>1224</td><td>742</td><td>509</td>
</tr>
<tr>
<td>HIGH</td><td>969</td><td>587</td><td>403</td>
</tr>
<tr>
<td rowspan="4">22</td>
<td rowspan="4">105 x 105</td>
<td>LOW</td><td>2409</td><td>1460</td><td>1003</td>
</tr>
<tr>
<td>MEDIUM</td><td>1872</td><td>1134</td><td>779</td>
</tr>
<tr>
<td>QUARTILE</td><td>1358</td><td>823</td><td>565</td>
</tr>
<tr>
<td>HIGH</td><td>1056</td><td>640</td><td>439</td>
</tr>
<tr>
<td rowspan="4">23</td>
<td rowspan="4">109 x 109</td>
<td>LOW</td><td>2620</td><td>1588</td><td>1091</td>
</tr>
<tr>
<td>MEDIUM</td><td>2059</td><td>1248</td><td>857</td>
</tr>
<tr>
<td>QUARTILE</td><td>1468</td><td>890</td><td>611</td>
</tr>
<tr>
<td>HIGH</td><td>1108</td><td>672</td><td>461</td>
</tr>
<tr>
<td rowspan="4">24</td>
<td rowspan="4">113 x 113</td>
<td>LOW</td><td>2812</td><td>1704</td><td>1171</td>
</tr>
<tr>
<td>MEDIUM</td><td>2188</td><td>1326</td><td>911</td>
</tr>
<tr>
<td>QUARTILE</td><td>1588</td><td>963</td><td>661</td>
</tr>
<tr>
<td>HIGH</td><td>1228</td><td>744</td><td>511</td>
</tr>
<tr>
<td rowspan="4">25</td>
<td rowspan="4">117 x 117</td>
<td>LOW</td><td>3057</td><td>1853</td><td>1273</td>
</tr>
<tr>
<td>MEDIUM</td><td>2395</td><td>1451</td><td>997</td>
</tr>
<tr>
<td>QUARTILE</td><td>1718</td><td>1041</td><td>715</td>
</tr>
<tr>
<td>HIGH</td><td>1286</td><td>779</td><td>535</td>
</tr>
<tr>
<td rowspan="4">26</td>
<td rowspan="4">121 x 121</td>
<td>LOW</td><td>3283</td><td>1990</td><td>1367</td>
</tr>
<tr>
<td>MEDIUM</td><td>2544</td><td>1542</td><td>1059</td>
</tr>
<tr>
<td>QUARTILE</td><td>1804</td><td>1094</td><td>751</td>
</tr>
<tr>
<td>HIGH</td><td>1425</td><td>864</td><td>593</td>
</tr>
<tr>
<td rowspan="4">27</td>
<td rowspan="4">125 x 125</td>
<td>LOW</td><td>3517</td><td>2132</td><td>1465</td>
</tr>
<tr>
<td>MEDIUM</td><td>2701</td><td>1637</td><td>1125</td>
</tr>
<tr>
<td>QUARTILE</td><td>1933</td><td>1172</td><td>805</td>
</tr>
<tr>
<td>HIGH</td><td>1501</td><td>910</td><td>625</td>
</tr>
<tr>
<td rowspan="4">28</td>
<td rowspan="4">129 x 129</td>
<td>LOW</td><td>3669</td><td>2223</td><td>1528</td>
</tr>
<tr>
<td>MEDIUM</td><td>2857</td><td>1732</td><td>1190</td>
</tr>
<tr>
<td>QUARTILE</td><td>2085</td><td>1263</td><td>868</td>
</tr>
<tr>
<td>HIGH</td><td>1581</td><td>958</td><td>658</td>
</tr>
<tr>
<td rowspan="4">29</td>
<td rowspan="4">133 x 133</td>
<td>LOW</td><td>3909</td><td>2369</td><td>1628</td>
</tr>
<tr>
<td>MEDIUM</td><td>3035</td><td>1839</td><td>1264</td>
</tr>
<tr>
<td>QUARTILE</td><td>2181</td><td>1322</td><td>908</td>
</tr>
<tr>
<td>HIGH</td><td>1677</td><td>1016</td><td>698</td>
</tr>
<tr>
<td rowspan="4">30</td>
<td rowspan="4">137 x 137</td>
<td>LOW</td><td>4158</td><td>2520</td><td>1732</td>
</tr>
<tr>
<td>MEDIUM</td><td>3289</td><td>1994</td><td>1370</td>
</tr>
<tr>
<td>QUARTILE</td><td>2358</td><td>1429</td><td>982</td>
</tr>
<tr>
<td>HIGH</td><td>1782</td><td>1080</td><td>742</td>
</tr>
<tr>
<td rowspan="4">31</td>
<td rowspan="4">141 x 141</td>
<td>LOW</td><td>4417</td><td>2677</td><td>1840</td>
</tr>
<tr>
<td>MEDIUM</td><td>3486</td><td>2113</td><td>1452</td>
</tr>
<tr>
<td>QUARTILE</td><td>2473</td><td>1499</td><td>1030</td>
</tr>
<tr>
<td>HIGH</td><td>1897</td><td>1150</td><td>790</td>
</tr>
<tr>
<td rowspan="4">32</td>
<td rowspan="4">145 x 145</td>
<td>LOW</td><td>4686</td><td>2840</td><td>1952</td>
</tr>
<tr>
<td>MEDIUM</td><td>3693</td><td>2238</td><td>1538</td>
</tr>
<tr>
<td>QUARTILE</td><td>2670</td><td>1618</td><td>1112</td>
</tr>
<tr>
<td>HIGH</td><td>2022</td><td>1226</td><td>842</td>
</tr>
<tr>
<td rowspan="4">33</td>
<td rowspan="4">149 x 149</td>
<td>LOW</td><td>4965</td><td>3009</td><td>2068</td>
</tr>
<tr>
<td>MEDIUM</td><td>3909</td><td>2369</td><td>1628</td>
</tr>
<tr>
<td>QUARTILE</td><td>2805</td><td>1700</td><td>1168</td>
</tr>
<tr>
<td>HIGH</td><td>2157</td><td>1307</td><td>898</td>
</tr>
<tr>
<td rowspan="4">34</td>
<td rowspan="4">153 x 153</td>
<td>LOW</td><td>5253</td><td>3183</td><td>2188</td>
</tr>
<tr>
<td>MEDIUM</td><td>4134</td><td>2506</td><td>1722</td>
</tr>
<tr>
<td>QUARTILE</td><td>2949</td><td>1787</td><td>1228</td>
</tr>
<tr>
<td>HIGH</td><td>2301</td><td>1394</td><td>958</td>
</tr>
<tr>
<td rowspan="4">35</td>
<td rowspan="4">157 x 157</td>
<td>LOW</td><td>5529</td><td>3351</td><td>2303</td>
</tr>
<tr>
<td>MEDIUM</td><td>4343</td><td>2632</td><td>1809</td>
</tr>
<tr>
<td>QUARTILE</td><td>3081</td><td>1867</td><td>1283</td>
</tr>
<tr>
<td>HIGH</td><td>2361</td><td>1431</td><td>983</td>
</tr>
<tr>
<td rowspan="4">36</td>
<td rowspan="4">161 x 161</td>
<td>LOW</td><td>5836</td><td>3537</td><td>2431</td>
</tr>
<tr>
<td>MEDIUM</td><td>4588</td><td>2780</td><td>1911</td>
</tr>
<tr>
<td>QUARTILE</td><td>3244</td><td>1966</td><td>1351</td>
</tr>
<tr>
<td>HIGH</td><td>2524</td><td>1530</td><td>1051</td>
</tr>
<tr>
<td rowspan="4">37</td>
<td rowspan="4">165 x 165</td>
<td>LOW</td><td>6153</td><td>3729</td><td>2563</td>
</tr>
<tr>
<td>MEDIUM</td><td>4775</td><td>2894</td><td>1989</td>
</tr>
<tr>
<td>QUARTILE</td><td>3417</td><td>2071</td><td>1423</td>
</tr>
<tr>
<td>HIGH</td><td>2625</td><td>1591</td><td>1093</td>
</tr>
<tr>
<td rowspan="4">38</td>
<td rowspan="4">169 x 169</td>
<td>LOW</td><td>6479</td><td>3927</td><td>2699</td>
</tr>
<tr>
<td>MEDIUM</td><td>5039</td><td>3054</td><td>2099</td>
</tr>
<tr>
<td>QUARTILE</td><td>3599</td><td>2181</td><td>1499</td>
</tr>
<tr>
<td>HIGH</td><td>2735</td><td>1658</td><td>1139</td>
</tr>
<tr>
<td rowspan="4">39</td>
<td rowspan="4">173 x 173</td>
<td>LOW</td><td>6743</td><td>4087</td><td>2809</td>
</tr>
<tr>
<td>MEDIUM</td><td>5313</td><td>3220</td><td>2213</td>
</tr>
<tr>
<td>QUARTILE</td><td>3791</td><td>2298</td><td>1579</td>
</tr>
<tr>
<td>HIGH</td><td>2927</td><td>1774</td><td>1219</td>
</tr>
<tr>
<td rowspan="4">40</td>
<td rowspan="4">177 x 177</td>
<td>LOW</td><td>7089</td><td>4296</td><td>2953</td>
</tr>
<tr>
<td>MEDIUM</td><td>5596</td><td>3391</td><td>2331</td>
</tr>
<tr>
<td>QUARTILE</td><td>3993</td><td>2420</td><td>1663</td>
</tr>
<tr>
<td>HIGH</td><td>3057</td><td>1852</td><td>1273</td>
</tr>
</table>
Special Thanks
--------------
A HUGE thank you to [Project Nayuki](https://www.nayuki.io/) for the
[QR code C++ library](https://github.com/nayuki/QR-Code-generator/tree/master/cpp)
which was critical in development of this library.
License
-------
MIT License.

View File

@ -1,56 +0,0 @@
/**
* QRCode
*
* A quick example of generating a QR code.
*
* This prints the QR code to the serial monitor as solid blocks. Each module
* is two characters wide, since the monospace font used in the serial monitor
* is approximately twice as tall as wide.
*
*/
#include "qrcode.h"
void setup() {
Serial.begin(115200);
// Start time
uint32_t dt = millis();
// Create the QR code
QRCode qrcode;
uint8_t qrcodeData[qrcode_getBufferSize(3)];
qrcode_initText(&qrcode, qrcodeData, 3, 0, "HELLO WORLD");
// Delta time
dt = millis() - dt;
Serial.print("QR Code Generation Time: ");
Serial.print(dt);
Serial.print("\n");
// Top quiet zone
Serial.print("\n\n\n\n");
for (uint8_t y = 0; y < qrcode.size; y++) {
// Left quiet zone
Serial.print(" ");
// Each horizontal module
for (uint8_t x = 0; x < qrcode.size; x++) {
// Print each module (UTF-8 \u2588 is a solid block)
Serial.print(qrcode_getModule(&qrcode, x, y) ? "\u2588\u2588": " ");
}
Serial.print("\n");
}
// Bottom quiet zone
Serial.print("\n\n\n\n");
}
void loop() {
}

View File

@ -1,62 +0,0 @@
Data = [
["1", "41", "25", "17", "34", "20", "14","27", "16", "11","17", "10", "7"],
["2", "77", "47", "32", "63", "38", "26", "48", "29", "20", "34", "20", "14"],
["3", "127", "77", "53", "101", "61", "42", "77", "47", "32", "58", "35", "24"],
["4", "187", "114", "78", "149", "90", "62", "111", "67", "46", "82", "50", "34"],
["5", "255", "154", "106", "202", "122", "84", "144", "87", "60", "106", "64", "44"],
["6", "322", "195", "134", "255", "154", "106", "178", "108", "74", "139", "84", "58"],
["7", "370", "224", "154", "293", "178", "122", "207", "125", "86", "154", "93", "64"],
["8", "461", "279", "192", "365", "221", "152", "259", "157", "108", "202", "122", "84"],
["9", "552", "335", "230", "432", "262", "180", "312", "189", "130", "235", "143", "98"],
["10", "652", "395", "271", "513", "311", "213", "364", "221", "151", "288", "174", "119"],
["11", "772", "468", "321", "604", "366", "251", "427", "259", "177", "331", "200", "137"],
["12", "883", "535", "367", "691", "419", "287", "489", "296", "203", "374", "227", "155"],
["13", "1022", "619", "425", "796", "483", "331", "580", "352", "241", "427", "259", "177"],
["14", "1101", "667", "458", "871", "528", "362", "621", "376", "258", "468", "283", "194"],
["15", "1250", "758", "520", "991", "600", "412", "703", "426", "292", "530", "321", "220"],
["16", "1408", "854", "586", "1082", "656", "450", "775", "470", "322", "602", "365", "250"],
["17", "1548", "938", "644", "1212", "734", "504", "876", "531", "364", "674", "408", "280"],
["18", "1725", "1046", "718", "1346", "816", "560", "948", "574", "394", "746", "452", "310"],
["19", "1903", "1153", "792", "1500", "909", "624", "1063", "644", "442", "813", "493", "338"],
["20", "2061", "1249", "858", "1600", "970", "666", "1159", "702", "482", "919", "557", "382"],
["21", "2232", "1352", "929", "1708", "1035", "711", "1224", "742", "509", "969", "587", "403"],
["22", "2409", "1460", "1003", "1872", "1134", "779", "1358", "823", "565", "1056", "640", "439"],
["23", "2620", "1588", "1091", "2059", "1248", "857", "1468", "890", "611", "1108", "672", "461"],
["24", "2812", "1704", "1171", "2188", "1326", "911", "1588", "963", "661", "1228", "744", "511"],
["25", "3057", "1853", "1273", "2395", "1451", "997", "1718", "1041", "715", "1286", "779", "535"],
["26", "3283", "1990", "1367", "2544", "1542", "1059", "1804", "1094", "751", "1425", "864", "593"],
["27", "3517", "2132", "1465", "2701", "1637", "1125", "1933", "1172", "805", "1501", "910", "625"],
["28", "3669", "2223", "1528", "2857", "1732", "1190", "2085", "1263", "868", "1581", "958", "658"],
["29", "3909", "2369", "1628", "3035", "1839", "1264", "2181", "1322", "908", "1677", "1016", "698"],
["30", "4158", "2520", "1732", "3289", "1994", "1370", "2358", "1429", "982", "1782", "1080", "742"],
["31", "4417", "2677", "1840", "3486", "2113", "1452", "2473", "1499", "1030", "1897", "1150", "790"],
["32", "4686", "2840", "1952", "3693", "2238", "1538", "2670", "1618", "1112", "2022", "1226", "842"],
["33", "4965", "3009", "2068", "3909", "2369", "1628", "2805", "1700", "1168", "2157", "1307", "898"],
["34", "5253", "3183", "2188", "4134", "2506", "1722", "2949", "1787", "1228", "2301", "1394", "958"],
["35", "5529", "3351", "2303", "4343", "2632", "1809", "3081", "1867", "1283", "2361", "1431", "983"],
["36", "5836", "3537", "2431", "4588", "2780", "1911", "3244", "1966", "1351", "2524", "1530", "1051"],
["37", "6153", "3729", "2563", "4775", "2894", "1989", "3417", "2071", "1423", "2625", "1591", "1093"],
["38", "6479", "3927", "2699", "5039", "3054", "2099", "3599", "2181", "1499", "2735", "1658", "1139"],
["39", "6743", "4087", "2809", "5313", "3220", "2213", "3791", "2298", "1579", "2927", "1774", "1219"],
["40", "7089", "4296", "2953", "5596", "3391", "2331", "3993", "2420", "1663", "3057", "1852", "1273"],
]
Template = ''' <tr>
<td rowspan="4">%s</td>
<td rowspan="4">%s</td>
<td>LOW</td><td>%s</td><td>%s</td><td>%s</td>
</tr>
<tr>
<td>MEDIUM</td><td>%s</td><td>%s</td><td>%s</td>
</tr>
<tr>
<td>QUARTILE</td><td>%s</td><td>%s</td><td>%s</td>
</tr>
<tr>
<td>HIGH</td><td>%s</td><td>%s</td><td>%s</td>
</tr>'''
for data in Data:
data = data[:]
size = 4 * int(data[0]) + 17
data.insert(1, "%d x %d" % (size, size))
print Template % tuple(data)

View File

@ -1,31 +0,0 @@
# Datatypes (KEYWORD1)
bool KEYWORD1
uint8_t KEYWORD1
QRCode KEYWORD1
# Methods and Functions (KEYWORD2)
qrcode_getBufferSize KEYWORD2
qrcode_initText KEYWORD2
qrcode_initBytes KEYWORD2
qrcode_getModule KEYWORD2
# Instances (KEYWORD2)
# Constants (LITERAL1)
false LITERAL1
true LITERAL1
ECC_LOW LITERAL1
ECC_MEDIUM LITERAL1
ECC_QUARTILE LITERAL1
ECC_HIGH LITERAL1
MODE_NUMERIC LITERAL1
MODE_ALPHANUMERIC LITERAL1
MODE_BYTE LITERAL1

View File

@ -1,10 +0,0 @@
name=QRCode
version=0.0.1
author=Richard Moore <me@ricmoo.com>
maintainer=Richard Moore <me@ricmoo.com>
sentence=A simple QR code generation library.
paragraph=A simple QR code generation library.
category=Other
url=https://github.com/ricmoo/qrcode/
architectures=*
includes=qrcode.h

View File

@ -1,876 +0,0 @@
/**
* The MIT License (MIT)
*
* This library is written and maintained by Richard Moore.
* Major parts were derived from Project Nayuki's library.
*
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
* heavily inspired and compared against.
*
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
*/
#include "qrcode.h"
#include <stdlib.h>
#include <string.h>
#pragma mark - Error Correction Lookup tables
#if LOCK_VERSION == 0
static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = {
// 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
{ 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
{ 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
{ 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
{ 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
};
static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
// 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
{ 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
{ 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
{ 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
{ 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
};
static const uint16_t NUM_RAW_DATA_MODULES[40] = {
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523,
// 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587,
// 32, 33, 34, 35, 36, 37, 38, 39, 40
19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648
};
// @TODO: Put other LOCK_VERSIONS here
#elif LOCK_VERSION == 3
static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {
26, 15, 44, 36
};
static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {
1, 1, 2, 2
};
static const uint16_t NUM_RAW_DATA_MODULES = 567;
#else
#error Unsupported LOCK_VERSION (add it...)
#endif
static int max(int a, int b) {
if (a > b) { return a; }
return b;
}
/*
static int abs(int value) {
if (value < 0) { return -value; }
return value;
}
*/
#pragma mark - Mode testing and conversion
static int8_t getAlphanumeric(char c) {
if (c >= '0' && c <= '9') { return (c - '0'); }
if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); }
switch (c) {
case ' ': return 36;
case '$': return 37;
case '%': return 38;
case '*': return 39;
case '+': return 40;
case '-': return 41;
case '.': return 42;
case '/': return 43;
case ':': return 44;
}
return -1;
}
static bool isAlphanumeric(const char *text, uint16_t length) {
while (length != 0) {
if (getAlphanumeric(text[--length]) == -1) { return false; }
}
return true;
}
static bool isNumeric(const char *text, uint16_t length) {
while (length != 0) {
char c = text[--length];
if (c < '0' || c > '9') { return false; }
}
return true;
}
#pragma mark - Counting
// We store the following tightly packed (less 8) in modeInfo
// <=9 <=26 <= 40
// NUMERIC ( 10, 12, 14);
// ALPHANUMERIC ( 9, 11, 13);
// BYTE ( 8, 16, 16);
static char getModeBits(uint8_t version, uint8_t mode) {
// Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits
// hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2))
unsigned int modeInfo = 0x7bbb80a;
#if LOCK_VERSION == 0 || LOCK_VERSION > 9
if (version > 9) { modeInfo >>= 9; }
#endif
#if LOCK_VERSION == 0 || LOCK_VERSION > 26
if (version > 26) { modeInfo >>= 9; }
#endif
char result = 8 + ((modeInfo >> (3 * mode)) & 0x07);
if (result == 15) { result = 16; }
return result;
}
#pragma mark - BitBucket
typedef struct BitBucket {
uint32_t bitOffsetOrWidth;
uint16_t capacityBytes;
uint8_t *data;
} BitBucket;
/*
void bb_dump(BitBucket *bitBuffer) {
printf("Buffer: ");
for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) {
printf("%02x", bitBuffer->data[i]);
if ((i % 4) == 3) { printf(" "); }
}
printf("\n");
}
*/
static uint16_t bb_getGridSizeBytes(uint8_t size) {
return (((size * size) + 7) / 8);
}
static uint16_t bb_getBufferSizeBytes(uint32_t bits) {
return ((bits + 7) / 8);
}
static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) {
bitBuffer->bitOffsetOrWidth = 0;
bitBuffer->capacityBytes = capacityBytes;
bitBuffer->data = data;
memset(data, 0, bitBuffer->capacityBytes);
}
static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) {
bitGrid->bitOffsetOrWidth = size;
bitGrid->capacityBytes = bb_getGridSizeBytes(size);
bitGrid->data = data;
memset(data, 0, bitGrid->capacityBytes);
}
static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) {
uint32_t offset = bitBuffer->bitOffsetOrWidth;
for (int8_t i = length - 1; i >= 0; i--, offset++) {
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
}
bitBuffer->bitOffsetOrWidth = offset;
}
/*
void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) {
for (int8_t i = length - 1; i >= 0; i--, offset++) {
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
}
}
*/
static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
uint8_t mask = 1 << (7 - (offset & 0x07));
if (on) {
bitGrid->data[offset >> 3] |= mask;
} else {
bitGrid->data[offset >> 3] &= ~mask;
}
}
static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
uint8_t mask = 1 << (7 - (offset & 0x07));
bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0);
if (on ^ invert) {
bitGrid->data[offset >> 3] |= mask;
} else {
bitGrid->data[offset >> 3] &= ~mask;
}
}
static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) {
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
}
#pragma mark - Drawing Patterns
// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
// properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
// This means it is possible to apply a mask, undo it, and try another mask. Note that a final
// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) {
uint8_t size = modules->bitOffsetOrWidth;
for (uint8_t y = 0; y < size; y++) {
for (uint8_t x = 0; x < size; x++) {
if (bb_getBit(isFunction, x, y)) { continue; }
bool invert = 0;
switch (mask) {
case 0: invert = (x + y) % 2 == 0; break;
case 1: invert = y % 2 == 0; break;
case 2: invert = x % 3 == 0; break;
case 3: invert = (x + y) % 3 == 0; break;
case 4: invert = (x / 3 + y / 2) % 2 == 0; break;
case 5: invert = x * y % 2 + x * y % 3 == 0; break;
case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;
case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;
}
bb_invertBit(modules, x, y, invert);
}
}
}
static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) {
bb_setBit(modules, x, y, on);
bb_setBit(isFunction, x, y, true);
}
// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
uint8_t size = modules->bitOffsetOrWidth;
for (int8_t i = -4; i <= 4; i++) {
for (int8_t j = -4; j <= 4; j++) {
uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm
int16_t xx = x + j, yy = y + i;
if (0 <= xx && xx < size && 0 <= yy && yy < size) {
setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4);
}
}
}
}
// Draws a 5*5 alignment pattern, with the center module at (x, y).
static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
for (int8_t i = -2; i <= 2; i++) {
for (int8_t j = -2; j <= 2; j++) {
setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1);
}
}
}
// Draws two copies of the format bits (with its own error correction code)
// based on the given mask and this object's error correction level field.
static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) {
uint8_t size = modules->bitOffsetOrWidth;
// Calculate error correction code and pack bits
uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3
uint32_t rem = data;
for (int i = 0; i < 10; i++) {
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
}
data = data << 10 | rem;
data ^= 0x5412; // uint15
// Draw first copy
for (uint8_t i = 0; i <= 5; i++) {
setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0);
}
setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0);
setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0);
setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0);
for (int8_t i = 9; i < 15; i++) {
setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0);
}
// Draw second copy
for (int8_t i = 0; i <= 7; i++) {
setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0);
}
for (int8_t i = 8; i < 15; i++) {
setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0);
}
setFunctionModule(modules, isFunction, 8, size - 8, true);
}
// Draws two copies of the version bits (with its own error correction code),
// based on this object's version field (which only has an effect for 7 <= version <= 40).
static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) {
int8_t size = modules->bitOffsetOrWidth;
#if LOCK_VERSION != 0 && LOCK_VERSION < 7
return;
#else
if (version < 7) { return; }
// Calculate error correction code and pack bits
uint32_t rem = version; // version is uint6, in the range [7, 40]
for (uint8_t i = 0; i < 12; i++) {
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
}
uint32_t data = version << 12 | rem; // uint18
// Draw two copies
for (uint8_t i = 0; i < 18; i++) {
bool bit = ((data >> i) & 1) != 0;
uint8_t a = size - 11 + i % 3, b = i / 3;
setFunctionModule(modules, isFunction, a, b, bit);
setFunctionModule(modules, isFunction, b, a, bit);
}
#endif
}
static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) {
uint8_t size = modules->bitOffsetOrWidth;
// Draw the horizontal and vertical timing patterns
for (uint8_t i = 0; i < size; i++) {
setFunctionModule(modules, isFunction, 6, i, i % 2 == 0);
setFunctionModule(modules, isFunction, i, 6, i % 2 == 0);
}
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
drawFinderPattern(modules, isFunction, 3, 3);
drawFinderPattern(modules, isFunction, size - 4, 3);
drawFinderPattern(modules, isFunction, 3, size - 4);
#if LOCK_VERSION == 0 || LOCK_VERSION > 1
if (version > 1) {
// Draw the numerous alignment patterns
uint8_t alignCount = version / 7 + 2;
uint8_t step;
if (version != 32) {
step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2
} else { // C-C-C-Combo breaker!
step = 26;
}
uint8_t alignPositionIndex = alignCount - 1;
uint8_t alignPosition[alignCount];
alignPosition[0] = 6;
uint8_t size = version * 4 + 17;
for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) {
alignPosition[alignPositionIndex--] = pos;
}
for (uint8_t i = 0; i < alignCount; i++) {
for (uint8_t j = 0; j < alignCount; j++) {
if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) {
continue; // Skip the three finder corners
} else {
drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]);
}
}
}
}
#endif
// Draw configuration data
drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor
drawVersion(modules, isFunction, version);
}
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
// data area of this QR Code symbol. Function modules need to be marked off before this is called.
static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) {
uint32_t bitLength = codewords->bitOffsetOrWidth;
uint8_t *data = codewords->data;
uint8_t size = modules->bitOffsetOrWidth;
// Bit index into the data
uint32_t i = 0;
// Do the funny zigzag scan
for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
if (right == 6) { right = 5; }
for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter
for (int j = 0; j < 2; j++) {
uint8_t x = right - j; // Actual x coordinate
bool upwards = ((right & 2) == 0) ^ (x < 6);
uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate
if (!bb_getBit(isFunction, x, y) && i < bitLength) {
bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0);
i++;
}
// If there are any remainder bits (0 to 7), they are already
// set to 0/false/white when the grid of modules was initialized
}
}
}
}
#pragma mark - Penalty Calculation
#define PENALTY_N1 3
#define PENALTY_N2 3
#define PENALTY_N3 40
#define PENALTY_N4 10
// Calculates and returns the penalty score based on state of this QR Code's current modules.
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
// @TODO: This can be optimized by working with the bytes instead of bits.
static uint32_t getPenaltyScore(BitBucket *modules) {
uint32_t result = 0;
uint8_t size = modules->bitOffsetOrWidth;
// Adjacent modules in row having same color
for (uint8_t y = 0; y < size; y++) {
bool colorX = bb_getBit(modules, 0, y);
for (uint8_t x = 1, runX = 1; x < size; x++) {
bool cx = bb_getBit(modules, x, y);
if (cx != colorX) {
colorX = cx;
runX = 1;
} else {
runX++;
if (runX == 5) {
result += PENALTY_N1;
} else if (runX > 5) {
result++;
}
}
}
}
// Adjacent modules in column having same color
for (uint8_t x = 0; x < size; x++) {
bool colorY = bb_getBit(modules, x, 0);
for (uint8_t y = 1, runY = 1; y < size; y++) {
bool cy = bb_getBit(modules, x, y);
if (cy != colorY) {
colorY = cy;
runY = 1;
} else {
runY++;
if (runY == 5) {
result += PENALTY_N1;
} else if (runY > 5) {
result++;
}
}
}
}
uint16_t black = 0;
for (uint8_t y = 0; y < size; y++) {
uint16_t bitsRow = 0, bitsCol = 0;
for (uint8_t x = 0; x < size; x++) {
bool color = bb_getBit(modules, x, y);
// 2*2 blocks of modules having same color
if (x > 0 && y > 0) {
bool colorUL = bb_getBit(modules, x - 1, y - 1);
bool colorUR = bb_getBit(modules, x, y - 1);
bool colorL = bb_getBit(modules, x - 1, y);
if (color == colorUL && color == colorUR && color == colorL) {
result += PENALTY_N2;
}
}
// Finder-like pattern in rows and columns
bitsRow = ((bitsRow << 1) & 0x7FF) | color;
bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x);
// Needs 11 bits accumulated
if (x >= 10) {
if (bitsRow == 0x05D || bitsRow == 0x5D0) {
result += PENALTY_N3;
}
if (bitsCol == 0x05D || bitsCol == 0x5D0) {
result += PENALTY_N3;
}
}
// Balance of black and white modules
if (color) { black++; }
}
}
// Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
uint16_t total = size * size;
for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) {
result += PENALTY_N4;
}
return result;
}
#pragma mark - Reed-Solomon Generator
static uint8_t rs_multiply(uint8_t x, uint8_t y) {
// Russian peasant multiplication
// See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
uint16_t z = 0;
for (int8_t i = 7; i >= 0; i--) {
z = (z << 1) ^ ((z >> 7) * 0x11D);
z ^= ((y >> i) & 1) * x;
}
return z;
}
static void rs_init(uint8_t degree, uint8_t *coeff) {
memset(coeff, 0, degree);
coeff[degree - 1] = 1;
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
// drop the highest term, and store the rest of the coefficients in order of descending powers.
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
uint16_t root = 1;
for (uint8_t i = 0; i < degree; i++) {
// Multiply the current product by (x - r^i)
for (uint8_t j = 0; j < degree; j++) {
coeff[j] = rs_multiply(coeff[j], root);
if (j + 1 < degree) {
coeff[j] ^= coeff[j + 1];
}
}
root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D)
}
}
static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) {
// Compute the remainder by performing polynomial division
//for (uint8_t i = 0; i < degree; i++) { result[] = 0; }
//memset(result, 0, degree);
for (uint8_t i = 0; i < length; i++) {
uint8_t factor = data[i] ^ result[0];
for (uint8_t j = 1; j < degree; j++) {
result[(j - 1) * stride] = result[j * stride];
}
result[(degree - 1) * stride] = 0;
for (uint8_t j = 0; j < degree; j++) {
result[j * stride] ^= rs_multiply(coeff[j], factor);
}
}
}
#pragma mark - QrCode
static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) {
int8_t mode = MODE_BYTE;
if (isNumeric((char*)text, length)) {
mode = MODE_NUMERIC;
bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC));
uint16_t accumData = 0;
uint8_t accumCount = 0;
for (uint16_t i = 0; i < length; i++) {
accumData = accumData * 10 + ((char)(text[i]) - '0');
accumCount++;
if (accumCount == 3) {
bb_appendBits(dataCodewords, accumData, 10);
accumData = 0;
accumCount = 0;
}
}
// 1 or 2 digits remaining
if (accumCount > 0) {
bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1);
}
} else if (isAlphanumeric((char*)text, length)) {
mode = MODE_ALPHANUMERIC;
bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC));
uint16_t accumData = 0;
uint8_t accumCount = 0;
for (uint16_t i = 0; i < length; i++) {
accumData = accumData * 45 + getAlphanumeric((char)(text[i]));
accumCount++;
if (accumCount == 2) {
bb_appendBits(dataCodewords, accumData, 11);
accumData = 0;
accumCount = 0;
}
}
// 1 character remaining
if (accumCount > 0) {
bb_appendBits(dataCodewords, accumData, 6);
}
} else {
bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4);
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE));
for (uint16_t i = 0; i < length; i++) {
bb_appendBits(dataCodewords, (char)(text[i]), 8);
}
}
//bb_setBits(dataCodewords, length, 4, getModeBits(version, mode));
return mode;
}
static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) {
// See: http://www.thonky.com/qr-code-tutorial/structure-final-message
#if LOCK_VERSION == 0
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1];
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1];
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
#else
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc];
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc];
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
#endif
uint8_t blockEccLen = totalEcc / numBlocks;
uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks;
uint8_t shortBlockLen = moduleCount / 8 / numBlocks;
uint8_t shortDataBlockLen = shortBlockLen - blockEccLen;
uint8_t result[data->capacityBytes];
memset(result, 0, sizeof(result));
uint8_t coeff[blockEccLen];
rs_init(blockEccLen, coeff);
uint16_t offset = 0;
uint8_t *dataBytes = data->data;
// Interleave all short blocks
for (uint8_t i = 0; i < shortDataBlockLen; i++) {
uint16_t index = i;
uint8_t stride = shortDataBlockLen;
for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
result[offset++] = dataBytes[index];
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
if (blockNum == numShortBlocks) { stride++; }
#endif
index += stride;
}
}
// Version less than 5 only have short blocks
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
{
// Interleave long blocks
uint16_t index = shortDataBlockLen * (numShortBlocks + 1);
uint8_t stride = shortDataBlockLen;
for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) {
result[offset++] = dataBytes[index];
if (blockNum == 0) { stride++; }
index += stride;
}
}
#endif
// Add all ecc blocks, interleaved
uint8_t blockSize = shortDataBlockLen;
for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
if (blockNum == numShortBlocks) { blockSize++; }
#endif
rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks);
dataBytes += blockSize;
}
memcpy(data->data, result, data->capacityBytes);
data->bitOffsetOrWidth = moduleCount;
}
// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits)
// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc)
static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0);
#pragma mark - Public QRCode functions
uint16_t qrcode_getBufferSize(uint8_t version) {
return bb_getGridSizeBytes(4 * version + 17);
}
// @TODO: Return error if data is too big.
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) {
uint8_t size = version * 4 + 17;
qrcode->version = version;
qrcode->size = size;
qrcode->ecc = ecc;
qrcode->modules = modules;
uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03;
#if LOCK_VERSION == 0
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1];
#else
version = LOCK_VERSION;
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits];
#endif
struct BitBucket codewords;
uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));
// Place the data code words into the buffer
int8_t mode = encodeDataCodewords(&codewords, data, length, version);
if (mode < 0) { return -1; }
qrcode->mode = mode;
// Add terminator and pad up to a byte if applicable
uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth;
if (padding > 4) { padding = 4; }
bb_appendBits(&codewords, 0, padding);
bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8);
// Pad with alternate bytes until data capacity is reached
for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) {
bb_appendBits(&codewords, padByte, 8);
}
BitBucket modulesGrid;
bb_initGrid(&modulesGrid, modules, size);
BitBucket isFunctionGrid;
uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
// Draw function patterns, draw all codewords, do masking
drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits);
performErrorCorrection(version, eccFormatBits, &codewords);
drawCodewords(&modulesGrid, &isFunctionGrid, &codewords);
// Find the best (lowest penalty) mask
uint8_t mask = 0;
int32_t minPenalty = INT32_MAX;
for (uint8_t i = 0; i < 8; i++) {
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i);
applyMask(&modulesGrid, &isFunctionGrid, i);
int penalty = getPenaltyScore(&modulesGrid);
if (penalty < minPenalty) {
mask = i;
minPenalty = penalty;
}
applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR
}
qrcode->mask = mask;
// Overwrite old format bits
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask);
// Apply the final choice of mask
applyMask(&modulesGrid, &isFunctionGrid, mask);
return 0;
}
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) {
return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data));
}
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) {
if (x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) {
return false;
}
uint32_t offset = y * qrcode->size + x;
return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
}
/*
uint8_t qrcode_getHexLength(QRCode *qrcode) {
return ((qrcode->size * qrcode->size) + 7) / 4;
}
void qrcode_getHex(QRCode *qrcode, char *result) {
}
*/

View File

@ -1,99 +0,0 @@
/**
* The MIT License (MIT)
*
* This library is written and maintained by Richard Moore.
* Major parts were derived from Project Nayuki's library.
*
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
* heavily inspired and compared against.
*
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
*/
#ifndef __QRCODE_H_
#define __QRCODE_H_
#ifndef __cplusplus
typedef unsigned char bool;
static const bool false = 0;
static const bool true = 1;
#endif
#include <stdint.h>
// QR Code Format Encoding
#define MODE_NUMERIC 0
#define MODE_ALPHANUMERIC 1
#define MODE_BYTE 2
// Error Correction Code Levels
#define ECC_LOW 0
#define ECC_MEDIUM 1
#define ECC_QUARTILE 2
#define ECC_HIGH 3
// If set to non-zero, this library can ONLY produce QR codes at that version
// This saves a lot of dynamic memory, as the codeword tables are skipped
#ifndef LOCK_VERSION
#define LOCK_VERSION 0
#endif
typedef struct QRCode {
uint8_t version;
uint8_t size;
uint8_t ecc;
uint8_t mode;
uint8_t mask;
uint8_t *modules;
} QRCode;
#ifdef __cplusplus
extern "C"{
#endif /* __cplusplus */
uint16_t qrcode_getBufferSize(uint8_t version);
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data);
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length);
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __QRCODE_H_ */