Fix NFC controller auth and document breadboard wiring
This commit is contained in:
173
docs/assets/esp32-pn532-breadboard.svg
Normal file
173
docs/assets/esp32-pn532-breadboard.svg
Normal file
@@ -0,0 +1,173 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1040" viewBox="0 0 1600 1040" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Breadboard wiring diagram for XIAO ESP32-S3 and PN532</title>
|
||||
<desc id="desc">Breadboard layout showing a XIAO ESP32-S3 wired to a PN532 NFC module in I2C mode with 3.3 volt power, ground, SDA, SCL, IRQ, and reset connections.</desc>
|
||||
<defs>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="16" stdDeviation="18" flood-color="#000000" flood-opacity="0.45"/>
|
||||
</filter>
|
||||
<linearGradient id="breadboard" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#f6f1e7"/>
|
||||
<stop offset="1" stop-color="#dfd5c6"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="xiaoBoard" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#173f55"/>
|
||||
<stop offset="1" stop-color="#071d28"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="pnBoard" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#0b765f"/>
|
||||
<stop offset="1" stop-color="#064236"/>
|
||||
</linearGradient>
|
||||
<style>
|
||||
.bg { fill: #070707; }
|
||||
.title { fill: #fafafa; font: 800 36px Inter, Arial, sans-serif; letter-spacing: 0.06em; }
|
||||
.subtitle { fill: #b8b8b8; font: 600 17px Inter, Arial, sans-serif; letter-spacing: 0.07em; }
|
||||
.boardLabel { fill: #fafafa; font: 800 20px Inter, Arial, sans-serif; letter-spacing: 0.08em; }
|
||||
.small { fill: #d4d4d4; font: 600 14px Inter, Arial, sans-serif; }
|
||||
.tiny { fill: #5b5148; font: 700 12px Inter, Arial, sans-serif; }
|
||||
.pinText { fill: #fafafa; font: 800 14px Inter, Arial, sans-serif; letter-spacing: 0.04em; }
|
||||
.hole { fill: #6b625a; opacity: 0.78; }
|
||||
.railHole { fill: #685f58; opacity: 0.72; }
|
||||
.railText { font: 800 16px Inter, Arial, sans-serif; }
|
||||
.xiao { fill: url(#xiaoBoard); stroke: #7dd3fc; stroke-opacity: 0.4; stroke-width: 3; }
|
||||
.pn532 { fill: url(#pnBoard); stroke: #67e8c9; stroke-opacity: 0.4; stroke-width: 3; }
|
||||
.chip { fill: #171717; stroke: #4b5563; stroke-width: 2; }
|
||||
.pinPad { fill: #171717; stroke: #f4f4f5; stroke-opacity: 0.35; stroke-width: 2; }
|
||||
.wire { fill: none; stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.wireHalo { fill: none; stroke: #ffffff; stroke-width: 15; stroke-linecap: round; stroke-linejoin: round; opacity: 0.2; }
|
||||
.tag { fill: #111111; stroke-width: 2; }
|
||||
.tagText { fill: #fafafa; font: 800 14px Inter, Arial, sans-serif; letter-spacing: 0.07em; }
|
||||
.noteBox { fill: #111111; stroke: #f2ad24; stroke-width: 2; }
|
||||
.noteTitle { fill: #f2ad24; font: 800 17px Inter, Arial, sans-serif; letter-spacing: 0.08em; }
|
||||
.note { fill: #d4d4d4; font: 600 15px Inter, Arial, sans-serif; }
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" width="1600" height="1040"/>
|
||||
<text class="title" x="70" y="70">BREADBOARD WIRING: XIAO ESP32-S3 + PN532</text>
|
||||
<text class="subtitle" x="72" y="104">PN532 IN I2C MODE, POWERED FROM THE XIAO 3V3 PIN</text>
|
||||
|
||||
<!-- Breadboard body -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="90" y="155" width="980" height="735" rx="28" fill="url(#breadboard)" stroke="#c9bba8" stroke-width="4"/>
|
||||
<rect x="132" y="197" width="896" height="78" rx="12" fill="#eee6db" stroke="#cfc2b4" stroke-width="2"/>
|
||||
<rect x="132" y="770" width="896" height="78" rx="12" fill="#eee6db" stroke="#cfc2b4" stroke-width="2"/>
|
||||
<rect x="132" y="336" width="896" height="90" rx="8" fill="#e6dbcd"/>
|
||||
<rect x="132" y="468" width="896" height="90" rx="8" fill="#e6dbcd"/>
|
||||
<rect x="132" y="426" width="896" height="42" fill="#cfc2b4" opacity="0.9"/>
|
||||
<text class="tiny" x="540" y="453">CENTER GAP</text>
|
||||
|
||||
<text class="railText" x="152" y="222" fill="#d92d20">+</text>
|
||||
<text class="railText" x="152" y="258" fill="#1d4ed8">-</text>
|
||||
<text class="railText" x="152" y="795" fill="#d92d20">+</text>
|
||||
<text class="railText" x="152" y="831" fill="#1d4ed8">-</text>
|
||||
|
||||
<!-- Power rail holes -->
|
||||
<g>
|
||||
<circle class="railHole" cx="202" cy="217" r="5"/><circle class="railHole" cx="238" cy="217" r="5"/><circle class="railHole" cx="274" cy="217" r="5"/><circle class="railHole" cx="310" cy="217" r="5"/><circle class="railHole" cx="346" cy="217" r="5"/><circle class="railHole" cx="382" cy="217" r="5"/><circle class="railHole" cx="418" cy="217" r="5"/><circle class="railHole" cx="454" cy="217" r="5"/><circle class="railHole" cx="490" cy="217" r="5"/><circle class="railHole" cx="526" cy="217" r="5"/><circle class="railHole" cx="562" cy="217" r="5"/><circle class="railHole" cx="598" cy="217" r="5"/><circle class="railHole" cx="634" cy="217" r="5"/><circle class="railHole" cx="670" cy="217" r="5"/><circle class="railHole" cx="706" cy="217" r="5"/><circle class="railHole" cx="742" cy="217" r="5"/><circle class="railHole" cx="778" cy="217" r="5"/><circle class="railHole" cx="814" cy="217" r="5"/><circle class="railHole" cx="850" cy="217" r="5"/><circle class="railHole" cx="886" cy="217" r="5"/><circle class="railHole" cx="922" cy="217" r="5"/><circle class="railHole" cx="958" cy="217" r="5"/>
|
||||
<circle class="railHole" cx="202" cy="253" r="5"/><circle class="railHole" cx="238" cy="253" r="5"/><circle class="railHole" cx="274" cy="253" r="5"/><circle class="railHole" cx="310" cy="253" r="5"/><circle class="railHole" cx="346" cy="253" r="5"/><circle class="railHole" cx="382" cy="253" r="5"/><circle class="railHole" cx="418" cy="253" r="5"/><circle class="railHole" cx="454" cy="253" r="5"/><circle class="railHole" cx="490" cy="253" r="5"/><circle class="railHole" cx="526" cy="253" r="5"/><circle class="railHole" cx="562" cy="253" r="5"/><circle class="railHole" cx="598" cy="253" r="5"/><circle class="railHole" cx="634" cy="253" r="5"/><circle class="railHole" cx="670" cy="253" r="5"/><circle class="railHole" cx="706" cy="253" r="5"/><circle class="railHole" cx="742" cy="253" r="5"/><circle class="railHole" cx="778" cy="253" r="5"/><circle class="railHole" cx="814" cy="253" r="5"/><circle class="railHole" cx="850" cy="253" r="5"/><circle class="railHole" cx="886" cy="253" r="5"/><circle class="railHole" cx="922" cy="253" r="5"/><circle class="railHole" cx="958" cy="253" r="5"/>
|
||||
<circle class="railHole" cx="202" cy="791" r="5"/><circle class="railHole" cx="238" cy="791" r="5"/><circle class="railHole" cx="274" cy="791" r="5"/><circle class="railHole" cx="310" cy="791" r="5"/><circle class="railHole" cx="346" cy="791" r="5"/><circle class="railHole" cx="382" cy="791" r="5"/><circle class="railHole" cx="418" cy="791" r="5"/><circle class="railHole" cx="454" cy="791" r="5"/><circle class="railHole" cx="490" cy="791" r="5"/><circle class="railHole" cx="526" cy="791" r="5"/><circle class="railHole" cx="562" cy="791" r="5"/><circle class="railHole" cx="598" cy="791" r="5"/><circle class="railHole" cx="634" cy="791" r="5"/><circle class="railHole" cx="670" cy="791" r="5"/><circle class="railHole" cx="706" cy="791" r="5"/><circle class="railHole" cx="742" cy="791" r="5"/><circle class="railHole" cx="778" cy="791" r="5"/><circle class="railHole" cx="814" cy="791" r="5"/><circle class="railHole" cx="850" cy="791" r="5"/><circle class="railHole" cx="886" cy="791" r="5"/><circle class="railHole" cx="922" cy="791" r="5"/><circle class="railHole" cx="958" cy="791" r="5"/>
|
||||
<circle class="railHole" cx="202" cy="827" r="5"/><circle class="railHole" cx="238" cy="827" r="5"/><circle class="railHole" cx="274" cy="827" r="5"/><circle class="railHole" cx="310" cy="827" r="5"/><circle class="railHole" cx="346" cy="827" r="5"/><circle class="railHole" cx="382" cy="827" r="5"/><circle class="railHole" cx="418" cy="827" r="5"/><circle class="railHole" cx="454" cy="827" r="5"/><circle class="railHole" cx="490" cy="827" r="5"/><circle class="railHole" cx="526" cy="827" r="5"/><circle class="railHole" cx="562" cy="827" r="5"/><circle class="railHole" cx="598" cy="827" r="5"/><circle class="railHole" cx="634" cy="827" r="5"/><circle class="railHole" cx="670" cy="827" r="5"/><circle class="railHole" cx="706" cy="827" r="5"/><circle class="railHole" cx="742" cy="827" r="5"/><circle class="railHole" cx="778" cy="827" r="5"/><circle class="railHole" cx="814" cy="827" r="5"/><circle class="railHole" cx="850" cy="827" r="5"/><circle class="railHole" cx="886" cy="827" r="5"/><circle class="railHole" cx="922" cy="827" r="5"/><circle class="railHole" cx="958" cy="827" r="5"/>
|
||||
</g>
|
||||
|
||||
<!-- Breadboard terminal holes -->
|
||||
<g>
|
||||
<circle class="hole" cx="202" cy="356" r="5"/><circle class="hole" cx="202" cy="378" r="5"/><circle class="hole" cx="202" cy="400" r="5"/><circle class="hole" cx="202" cy="488" r="5"/><circle class="hole" cx="202" cy="510" r="5"/><circle class="hole" cx="202" cy="532" r="5"/>
|
||||
<circle class="hole" cx="238" cy="356" r="5"/><circle class="hole" cx="238" cy="378" r="5"/><circle class="hole" cx="238" cy="400" r="5"/><circle class="hole" cx="238" cy="488" r="5"/><circle class="hole" cx="238" cy="510" r="5"/><circle class="hole" cx="238" cy="532" r="5"/>
|
||||
<circle class="hole" cx="274" cy="356" r="5"/><circle class="hole" cx="274" cy="378" r="5"/><circle class="hole" cx="274" cy="400" r="5"/><circle class="hole" cx="274" cy="488" r="5"/><circle class="hole" cx="274" cy="510" r="5"/><circle class="hole" cx="274" cy="532" r="5"/>
|
||||
<circle class="hole" cx="310" cy="356" r="5"/><circle class="hole" cx="310" cy="378" r="5"/><circle class="hole" cx="310" cy="400" r="5"/><circle class="hole" cx="310" cy="488" r="5"/><circle class="hole" cx="310" cy="510" r="5"/><circle class="hole" cx="310" cy="532" r="5"/>
|
||||
<circle class="hole" cx="346" cy="356" r="5"/><circle class="hole" cx="346" cy="378" r="5"/><circle class="hole" cx="346" cy="400" r="5"/><circle class="hole" cx="346" cy="488" r="5"/><circle class="hole" cx="346" cy="510" r="5"/><circle class="hole" cx="346" cy="532" r="5"/>
|
||||
<circle class="hole" cx="382" cy="356" r="5"/><circle class="hole" cx="382" cy="378" r="5"/><circle class="hole" cx="382" cy="400" r="5"/><circle class="hole" cx="382" cy="488" r="5"/><circle class="hole" cx="382" cy="510" r="5"/><circle class="hole" cx="382" cy="532" r="5"/>
|
||||
<circle class="hole" cx="418" cy="356" r="5"/><circle class="hole" cx="418" cy="378" r="5"/><circle class="hole" cx="418" cy="400" r="5"/><circle class="hole" cx="418" cy="488" r="5"/><circle class="hole" cx="418" cy="510" r="5"/><circle class="hole" cx="418" cy="532" r="5"/>
|
||||
<circle class="hole" cx="454" cy="356" r="5"/><circle class="hole" cx="454" cy="378" r="5"/><circle class="hole" cx="454" cy="400" r="5"/><circle class="hole" cx="454" cy="488" r="5"/><circle class="hole" cx="454" cy="510" r="5"/><circle class="hole" cx="454" cy="532" r="5"/>
|
||||
<circle class="hole" cx="490" cy="356" r="5"/><circle class="hole" cx="490" cy="378" r="5"/><circle class="hole" cx="490" cy="400" r="5"/><circle class="hole" cx="490" cy="488" r="5"/><circle class="hole" cx="490" cy="510" r="5"/><circle class="hole" cx="490" cy="532" r="5"/>
|
||||
<circle class="hole" cx="526" cy="356" r="5"/><circle class="hole" cx="526" cy="378" r="5"/><circle class="hole" cx="526" cy="400" r="5"/><circle class="hole" cx="526" cy="488" r="5"/><circle class="hole" cx="526" cy="510" r="5"/><circle class="hole" cx="526" cy="532" r="5"/>
|
||||
<circle class="hole" cx="562" cy="356" r="5"/><circle class="hole" cx="562" cy="378" r="5"/><circle class="hole" cx="562" cy="400" r="5"/><circle class="hole" cx="562" cy="488" r="5"/><circle class="hole" cx="562" cy="510" r="5"/><circle class="hole" cx="562" cy="532" r="5"/>
|
||||
<circle class="hole" cx="598" cy="356" r="5"/><circle class="hole" cx="598" cy="378" r="5"/><circle class="hole" cx="598" cy="400" r="5"/><circle class="hole" cx="598" cy="488" r="5"/><circle class="hole" cx="598" cy="510" r="5"/><circle class="hole" cx="598" cy="532" r="5"/>
|
||||
<circle class="hole" cx="634" cy="356" r="5"/><circle class="hole" cx="634" cy="378" r="5"/><circle class="hole" cx="634" cy="400" r="5"/><circle class="hole" cx="634" cy="488" r="5"/><circle class="hole" cx="634" cy="510" r="5"/><circle class="hole" cx="634" cy="532" r="5"/>
|
||||
<circle class="hole" cx="670" cy="356" r="5"/><circle class="hole" cx="670" cy="378" r="5"/><circle class="hole" cx="670" cy="400" r="5"/><circle class="hole" cx="670" cy="488" r="5"/><circle class="hole" cx="670" cy="510" r="5"/><circle class="hole" cx="670" cy="532" r="5"/>
|
||||
<circle class="hole" cx="706" cy="356" r="5"/><circle class="hole" cx="706" cy="378" r="5"/><circle class="hole" cx="706" cy="400" r="5"/><circle class="hole" cx="706" cy="488" r="5"/><circle class="hole" cx="706" cy="510" r="5"/><circle class="hole" cx="706" cy="532" r="5"/>
|
||||
<circle class="hole" cx="742" cy="356" r="5"/><circle class="hole" cx="742" cy="378" r="5"/><circle class="hole" cx="742" cy="400" r="5"/><circle class="hole" cx="742" cy="488" r="5"/><circle class="hole" cx="742" cy="510" r="5"/><circle class="hole" cx="742" cy="532" r="5"/>
|
||||
<circle class="hole" cx="778" cy="356" r="5"/><circle class="hole" cx="778" cy="378" r="5"/><circle class="hole" cx="778" cy="400" r="5"/><circle class="hole" cx="778" cy="488" r="5"/><circle class="hole" cx="778" cy="510" r="5"/><circle class="hole" cx="778" cy="532" r="5"/>
|
||||
<circle class="hole" cx="814" cy="356" r="5"/><circle class="hole" cx="814" cy="378" r="5"/><circle class="hole" cx="814" cy="400" r="5"/><circle class="hole" cx="814" cy="488" r="5"/><circle class="hole" cx="814" cy="510" r="5"/><circle class="hole" cx="814" cy="532" r="5"/>
|
||||
<circle class="hole" cx="850" cy="356" r="5"/><circle class="hole" cx="850" cy="378" r="5"/><circle class="hole" cx="850" cy="400" r="5"/><circle class="hole" cx="850" cy="488" r="5"/><circle class="hole" cx="850" cy="510" r="5"/><circle class="hole" cx="850" cy="532" r="5"/>
|
||||
<circle class="hole" cx="886" cy="356" r="5"/><circle class="hole" cx="886" cy="378" r="5"/><circle class="hole" cx="886" cy="400" r="5"/><circle class="hole" cx="886" cy="488" r="5"/><circle class="hole" cx="886" cy="510" r="5"/><circle class="hole" cx="886" cy="532" r="5"/>
|
||||
<circle class="hole" cx="922" cy="356" r="5"/><circle class="hole" cx="922" cy="378" r="5"/><circle class="hole" cx="922" cy="400" r="5"/><circle class="hole" cx="922" cy="488" r="5"/><circle class="hole" cx="922" cy="510" r="5"/><circle class="hole" cx="922" cy="532" r="5"/>
|
||||
<circle class="hole" cx="958" cy="356" r="5"/><circle class="hole" cx="958" cy="378" r="5"/><circle class="hole" cx="958" cy="400" r="5"/><circle class="hole" cx="958" cy="488" r="5"/><circle class="hole" cx="958" cy="510" r="5"/><circle class="hole" cx="958" cy="532" r="5"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- XIAO module straddling breadboard center gap -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect class="xiao" x="318" y="300" width="238" height="288" rx="26"/>
|
||||
<rect x="383" y="322" width="108" height="42" rx="8" fill="#d8d8d8" stroke="#6b7280" stroke-width="2"/>
|
||||
<text class="small" x="409" y="349" fill="#111111">USB-C</text>
|
||||
<rect class="chip" x="384" y="408" width="106" height="78" rx="10"/>
|
||||
<text class="boardLabel" x="377" y="529">XIAO ESP32-S3</text>
|
||||
|
||||
<circle class="pinPad" cx="334" cy="340" r="11"/><text class="pinText" x="350" y="345">3V3</text>
|
||||
<circle class="pinPad" cx="334" cy="382" r="11"/><text class="pinText" x="350" y="387">GND</text>
|
||||
<circle class="pinPad" cx="334" cy="424" r="11"/><text class="pinText" x="350" y="429">D4 / GPIO5</text>
|
||||
<circle class="pinPad" cx="334" cy="466" r="11"/><text class="pinText" x="350" y="471">D5 / GPIO6</text>
|
||||
<circle class="pinPad" cx="334" cy="508" r="11"/><text class="pinText" x="350" y="513">D2 / GPIO3</text>
|
||||
<circle class="pinPad" cx="334" cy="550" r="11"/><text class="pinText" x="350" y="555">D3 / GPIO4</text>
|
||||
</g>
|
||||
|
||||
<!-- PN532 module to the side, pins facing the breadboard -->
|
||||
<g filter="url(#shadow)">
|
||||
<rect class="pn532" x="1156" y="206" width="344" height="500" rx="28"/>
|
||||
<text class="boardLabel" x="1248" y="258">PN532 NFC V3</text>
|
||||
<text class="small" x="1234" y="286">Set switches/jumpers to I2C</text>
|
||||
<rect x="1242" y="330" width="174" height="174" rx="22" fill="none" stroke="#d1fae5" stroke-opacity="0.22" stroke-width="9"/>
|
||||
<rect x="1269" y="357" width="120" height="120" rx="18" fill="none" stroke="#d1fae5" stroke-opacity="0.22" stroke-width="7"/>
|
||||
<rect class="chip" x="1270" y="548" width="116" height="58" rx="10"/>
|
||||
|
||||
<text class="pinText" x="1194" y="347" text-anchor="end">VCC</text><circle class="pinPad" cx="1166" cy="342" r="11"/>
|
||||
<text class="pinText" x="1194" y="389" text-anchor="end">GND</text><circle class="pinPad" cx="1166" cy="384" r="11"/>
|
||||
<text class="pinText" x="1194" y="431" text-anchor="end">SDA</text><circle class="pinPad" cx="1166" cy="426" r="11"/>
|
||||
<text class="pinText" x="1194" y="473" text-anchor="end">SCL</text><circle class="pinPad" cx="1166" cy="468" r="11"/>
|
||||
<text class="pinText" x="1194" y="515" text-anchor="end">IRQ</text><circle class="pinPad" cx="1166" cy="510" r="11"/>
|
||||
<text class="pinText" x="1194" y="557" text-anchor="end">RSTO/RST</text><circle class="pinPad" cx="1166" cy="552" r="11"/>
|
||||
</g>
|
||||
|
||||
<!-- Wires from XIAO through breadboard rows to PN532 -->
|
||||
<path class="wireHalo" d="M334 340 C430 224 690 207 958 217 C1070 222 1122 292 1166 342"/>
|
||||
<path class="wire" stroke="#e23a3a" d="M334 340 C430 224 690 207 958 217 C1070 222 1122 292 1166 342"/>
|
||||
|
||||
<path class="wireHalo" d="M334 382 C430 264 712 246 958 253 C1065 258 1120 336 1166 384"/>
|
||||
<path class="wire" stroke="#202020" d="M334 382 C430 264 712 246 958 253 C1065 258 1120 336 1166 384"/>
|
||||
|
||||
<path class="wireHalo" d="M334 424 C500 365 790 368 958 356 C1060 350 1120 397 1166 426"/>
|
||||
<path class="wire" stroke="#3f8cff" d="M334 424 C500 365 790 368 958 356 C1060 350 1120 397 1166 426"/>
|
||||
|
||||
<path class="wireHalo" d="M334 466 C502 420 790 394 958 378 C1065 370 1122 438 1166 468"/>
|
||||
<path class="wire" stroke="#ffd23c" d="M334 466 C502 420 790 394 958 378 C1065 370 1122 438 1166 468"/>
|
||||
|
||||
<path class="wireHalo" d="M334 508 C520 510 782 492 958 488 C1060 486 1125 496 1166 510"/>
|
||||
<path class="wire" stroke="#b96cff" d="M334 508 C520 510 782 492 958 488 C1060 486 1125 496 1166 510"/>
|
||||
|
||||
<path class="wireHalo" d="M334 550 C522 572 786 534 958 532 C1060 530 1125 548 1166 552"/>
|
||||
<path class="wire" stroke="#34d399" d="M334 550 C522 572 786 534 958 532 C1060 530 1125 548 1166 552"/>
|
||||
|
||||
<!-- Wire labels -->
|
||||
<g>
|
||||
<rect class="tag" x="624" y="170" width="142" height="34" rx="8" stroke="#e23a3a"/>
|
||||
<text class="tagText" x="642" y="193">3V3 -> VCC</text>
|
||||
<rect class="tag" x="624" y="280" width="144" height="34" rx="8" stroke="#202020"/>
|
||||
<text class="tagText" x="642" y="303">GND -> GND</text>
|
||||
<rect class="tag" x="610" y="606" width="174" height="34" rx="8" stroke="#3f8cff"/>
|
||||
<text class="tagText" x="628" y="629">D4 / GPIO5 -> SDA</text>
|
||||
<rect class="tag" x="802" y="606" width="174" height="34" rx="8" stroke="#ffd23c"/>
|
||||
<text class="tagText" x="820" y="629">D5 / GPIO6 -> SCL</text>
|
||||
<rect class="tag" x="610" y="650" width="154" height="34" rx="8" stroke="#b96cff"/>
|
||||
<text class="tagText" x="628" y="673">D2 / GPIO3 -> IRQ</text>
|
||||
<rect class="tag" x="802" y="650" width="186" height="34" rx="8" stroke="#34d399"/>
|
||||
<text class="tagText" x="820" y="673">D3 / GPIO4 -> RST</text>
|
||||
</g>
|
||||
|
||||
<!-- Callouts -->
|
||||
<g>
|
||||
<rect class="noteBox" x="90" y="922" width="1410" height="76" rx="16"/>
|
||||
<text class="noteTitle" x="122" y="953">CHECK BEFORE POWERING</text>
|
||||
<text class="note" x="122" y="980">Use 3.3V, not 5V. Keep the PN532 in I2C mode. Breadboard rows are illustrative; follow the same net names if your modules sit in different rows.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -115,6 +115,10 @@ Photo overlay using the supplied board pictures:
|
||||
|
||||

|
||||
|
||||
Breadboard layout:
|
||||
|
||||

|
||||
|
||||
Clean schematic:
|
||||
|
||||

|
||||
|
||||
@@ -31,6 +31,10 @@ Photo overlay using the supplied board pictures:
|
||||
|
||||

|
||||
|
||||
Breadboard layout:
|
||||
|
||||

|
||||
|
||||
Clean schematic:
|
||||
|
||||

|
||||
|
||||
@@ -14,6 +14,7 @@ Adafruit_PN532 nfc(PN532_IRQ_PIN, PN532_RESET_PIN, &Wire);
|
||||
|
||||
static unsigned long lastReadAt = 0;
|
||||
static String lastCredential = "";
|
||||
static bool wifiScanPrinted = false;
|
||||
|
||||
String bytesToHex(const uint8_t *data, uint8_t length) {
|
||||
String out;
|
||||
@@ -37,15 +38,94 @@ String jsonEscape(const String &value) {
|
||||
return out;
|
||||
}
|
||||
|
||||
const char *wifiStatusName(wl_status_t status) {
|
||||
switch (status) {
|
||||
case WL_IDLE_STATUS:
|
||||
return "idle";
|
||||
case WL_NO_SSID_AVAIL:
|
||||
return "ssid not available";
|
||||
case WL_SCAN_COMPLETED:
|
||||
return "scan completed";
|
||||
case WL_CONNECTED:
|
||||
return "connected";
|
||||
case WL_CONNECT_FAILED:
|
||||
return "connect failed";
|
||||
case WL_CONNECTION_LOST:
|
||||
return "connection lost";
|
||||
case WL_DISCONNECTED:
|
||||
return "disconnected";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char *encryptionName(wifi_auth_mode_t type) {
|
||||
switch (type) {
|
||||
case WIFI_AUTH_OPEN:
|
||||
return "open";
|
||||
case WIFI_AUTH_WEP:
|
||||
return "WEP";
|
||||
case WIFI_AUTH_WPA_PSK:
|
||||
return "WPA";
|
||||
case WIFI_AUTH_WPA2_PSK:
|
||||
return "WPA2";
|
||||
case WIFI_AUTH_WPA_WPA2_PSK:
|
||||
return "WPA/WPA2";
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE:
|
||||
return "WPA2 enterprise";
|
||||
case WIFI_AUTH_WPA3_PSK:
|
||||
return "WPA3";
|
||||
case WIFI_AUTH_WPA2_WPA3_PSK:
|
||||
return "WPA2/WPA3";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void printWifiScan() {
|
||||
if (wifiScanPrinted) return;
|
||||
wifiScanPrinted = true;
|
||||
|
||||
Serial.println("Scanning Wi-Fi networks...");
|
||||
int count = WiFi.scanNetworks(false, true);
|
||||
if (count < 0) {
|
||||
Serial.printf("Wi-Fi scan failed: %d\n", count);
|
||||
return;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (WiFi.SSID(i) != WIFI_SSID) continue;
|
||||
found = true;
|
||||
Serial.printf(
|
||||
"Found %s: RSSI %d dBm, channel %d, auth %s\n",
|
||||
WIFI_SSID,
|
||||
WiFi.RSSI(i),
|
||||
WiFi.channel(i),
|
||||
encryptionName(WiFi.encryptionType(i))
|
||||
);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Serial.printf("SSID %s was not found in scan.\n", WIFI_SSID);
|
||||
}
|
||||
}
|
||||
|
||||
void connectWifi() {
|
||||
if (WiFi.status() == WL_CONNECTED) return;
|
||||
|
||||
Serial.printf("Connecting to Wi-Fi %s", WIFI_SSID);
|
||||
WiFi.persistent(false);
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setSleep(false);
|
||||
printWifiScan();
|
||||
|
||||
Serial.printf("Connecting to Wi-Fi %s", WIFI_SSID);
|
||||
WiFi.disconnect(false, false);
|
||||
delay(100);
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
||||
|
||||
unsigned long startedAt = millis();
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - startedAt < 15000) {
|
||||
while (WiFi.status() != WL_CONNECTED && millis() - startedAt < 30000) {
|
||||
Serial.print(".");
|
||||
delay(350);
|
||||
}
|
||||
@@ -55,7 +135,8 @@ void connectWifi() {
|
||||
Serial.print("Wi-Fi connected: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
} else {
|
||||
Serial.println("Wi-Fi connection failed.");
|
||||
wl_status_t status = WiFi.status();
|
||||
Serial.printf("Wi-Fi connection failed: %s (%d).\n", wifiStatusName(status), status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1536,7 +1536,10 @@ const handleApi = async (req, res) => {
|
||||
if (!privilegedConnectionAllowed(req)) return json(res, 404, { error: 'Controller access is only available from the local connection.' })
|
||||
const controllerAuthed = hasControllerAuth(req)
|
||||
const isAdmin = (req.headers.authorization || '').startsWith('Bearer ')
|
||||
if (!controllerAuthed && (!isAdmin || !requireAdmin(req, res))) return
|
||||
if (!controllerAuthed) {
|
||||
if (!isAdmin) return json(res, 401, { error: 'Controller token is required.' })
|
||||
if (!requireAdmin(req, res)) return
|
||||
}
|
||||
const body = await readBody(req)
|
||||
const doorId = cleanText(body.doorId, 80) || 'front-door'
|
||||
const cardCredential = cleanText(body.cardCredential || body.uid, 160)
|
||||
|
||||
Reference in New Issue
Block a user