센서1·3(누수 노드)에 PMS7003 를 온습도와 동일 sensor_id 로 통합하고 metric_type='pms7003'
로 구분해 수집한다. LED 는 스위칭 없이 상시 점등 방식으로 확정.
- raspberry_pi4_pms7003/: PMS7003 노드 풀세트(드라이버·센서·install·systemd·vendor·배선·연결테스트·프레임 단위테스트)
- 서버: evaluate_metric_thresholds() 에 pm25/pm10 선택 인자 추가(온습도 전용 호출 후방호환),
sensor_data.php 수집 경로에 pms7003 metric 반영, config.php/예시에 PM 임계 추가
- SQL: migration_pms7003.sql · schema_sht30.sql 정합, tools/migration_leak_add_{pms7003,sht30} 마이그레이터
- tests/pm_threshold_test.php + run_tests.sh 러너 등록
- .gitignore: 노드별 config_local.py 커밋 차단
535 lines
28 KiB
HTML
535 lines
28 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>PM Node Wiring & Pinmap — PMS7003 UART (sensor_id=3 / Pi-C)</title>
|
|
<style>
|
|
:root{
|
|
--ink:#1a1d21; --muted:#5c6570; --line:#dfe3e8; --bg:#eef1f4; --card:#ffffff;
|
|
--accent:#1971c2; --accent-d:#155a9c; --warn:#e03131; --warnbg:#fff5f5;
|
|
--ok:#2f9e44; --gnd:#343a40; --chip:#eef2f6; --code:#0b3d2e;
|
|
--mono:"D2Coding","Consolas","Menlo","Courier New",ui-monospace,monospace;
|
|
--sans:"Malgun Gothic","Apple SD Gothic Neo","Noto Sans KR","Segoe UI",system-ui,-apple-system,sans-serif;
|
|
}
|
|
*{box-sizing:border-box;}
|
|
html,body{margin:0;padding:0;}
|
|
body{
|
|
font-family:var(--sans); color:var(--ink); background:var(--bg);
|
|
line-height:1.6; -webkit-font-smoothing:antialiased;
|
|
}
|
|
.wrap{max-width:1000px; margin:0 auto; padding:22px 18px 60px;}
|
|
|
|
/* ---------- Header ---------- */
|
|
.hero{
|
|
background:linear-gradient(135deg,#1f2933 0%,#283746 55%,#2f4a5a 100%);
|
|
color:#fff; border-radius:16px; padding:26px 28px;
|
|
box-shadow:0 8px 24px rgba(20,30,40,.18);
|
|
}
|
|
.hero .eyebrow{font-size:12.5px; letter-spacing:.14em; text-transform:uppercase;
|
|
color:#9fc3e0; font-weight:700; margin:0 0 6px;}
|
|
.hero h1{margin:0 0 8px; font-size:27px; line-height:1.25; font-weight:800;}
|
|
.hero p.sum{margin:0 0 16px; font-size:14.5px; color:#d7e2ec; max-width:72ch;}
|
|
.chips{display:flex; flex-wrap:wrap; gap:8px;}
|
|
.chip{
|
|
display:inline-flex; align-items:center; gap:6px;
|
|
background:rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.22);
|
|
color:#eef5fb; font-size:12.5px; font-weight:600; padding:5px 11px; border-radius:999px;
|
|
}
|
|
.chip.mono{font-family:var(--mono); letter-spacing:.01em;}
|
|
.chip b{color:#fff;}
|
|
|
|
/* ---------- Sections ---------- */
|
|
section{
|
|
background:var(--card); border:1px solid var(--line); border-radius:14px;
|
|
padding:22px 24px; margin-top:20px; box-shadow:0 2px 10px rgba(20,30,40,.05);
|
|
}
|
|
h2{
|
|
display:flex; align-items:center; gap:11px; margin:0 0 16px;
|
|
font-size:19px; font-weight:800; color:var(--ink);
|
|
}
|
|
h2 .n{
|
|
flex:0 0 auto; width:30px; height:30px; border-radius:9px;
|
|
background:var(--accent); color:#fff; font-size:15px; font-weight:800;
|
|
display:flex; align-items:center; justify-content:center;
|
|
}
|
|
h2 small{font-weight:600; font-size:13px; color:var(--muted);}
|
|
|
|
/* ---------- Pinmap SVG ---------- */
|
|
.svg-pin{display:block; width:100%; max-width:460px; height:auto; margin:2px auto 0;}
|
|
.diagram-cap{text-align:center; color:var(--muted); font-size:12.5px; margin:12px 0 0;}
|
|
.legend{
|
|
display:flex; flex-wrap:wrap; justify-content:center; gap:12px 18px;
|
|
margin:16px auto 2px; max-width:640px;
|
|
}
|
|
.legend span{display:inline-flex; align-items:center; gap:7px; font-size:12.5px; color:var(--ink);}
|
|
.sw{width:15px; height:15px; border-radius:50%; flex:0 0 auto; border:1px solid rgba(0,0,0,.15);}
|
|
.sw.pwr{background:#2f9e44;} .sw.uart{background:#1971c2;}
|
|
.sw.gnd{background:#343a40;}
|
|
.sw.opt{background:#ced4da; border:1.5px dashed #868e96;}
|
|
.sw.off{background:#dee2e6;}
|
|
|
|
/* SVG element classes */
|
|
.p-pwr{fill:#2f9e44; stroke:#248a3b; stroke-width:1.5;}
|
|
.p-uart{fill:#1971c2; stroke:#155a9c; stroke-width:1.5;}
|
|
.p-gnd{fill:#343a40; stroke:#1f2429; stroke-width:1.5;}
|
|
.p-opt{fill:#ced4da; stroke:#868e96; stroke-width:1.5; stroke-dasharray:3 2;}
|
|
.p-off{fill:#dee2e6; stroke:#c3c9cf; stroke-width:1;}
|
|
.rail{fill:#f1f3f5; stroke:#ced4da; stroke-width:1.5;}
|
|
.pnum{font-family:var(--mono); font-size:12px; font-weight:700; text-anchor:middle; dominant-baseline:central;}
|
|
.pnum-l{fill:#ffffff;} .pnum-d{fill:#495057;}
|
|
.plbl{font-family:var(--mono); font-size:12px; dominant-baseline:central;}
|
|
.lbl-l{text-anchor:end;} .lbl-r{text-anchor:start;}
|
|
.lbl-used{fill:#1a1d21; font-weight:700;}
|
|
.lbl-alt{fill:#5c6570; font-weight:600;}
|
|
.lbl-off{fill:#adb5bd; font-weight:500;}
|
|
.cap-svg{font-family:var(--sans); font-size:12.5px; fill:#5c6570; font-weight:600; text-anchor:middle;}
|
|
|
|
/* ---------- Table ---------- */
|
|
table{width:100%; border-collapse:collapse; font-size:14px;}
|
|
thead th{
|
|
background:#f1f4f7; color:#2b3541; text-align:left; font-weight:700;
|
|
padding:10px 12px; border-bottom:2px solid #ccd4dc; font-size:13px;
|
|
}
|
|
tbody td{padding:11px 12px; border-bottom:1px solid var(--line); vertical-align:top;}
|
|
tbody tr:last-child td{border-bottom:none;}
|
|
.mono{font-family:var(--mono);}
|
|
.pinbadge{
|
|
display:inline-block; min-width:26px; text-align:center; font-family:var(--mono);
|
|
font-weight:700; font-size:13px; color:#fff; background:var(--accent);
|
|
border-radius:6px; padding:2px 7px;
|
|
}
|
|
.pinbadge.gnd{background:var(--gnd);} .pinbadge.pwr{background:var(--ok);}
|
|
.pinbadge.na{background:#adb5bd;}
|
|
td .warn-inline{color:var(--warn); font-weight:700;}
|
|
|
|
/* ---------- Schematic ---------- */
|
|
.svg-sch{display:block; width:100%; max-width:840px; height:auto; margin:0 auto;}
|
|
.box{fill:#f8f9fa; stroke:#495057; stroke-width:2; rx:8;}
|
|
.boxtitle{font-family:var(--sans); font-size:14px; font-weight:800; fill:#1a1d21; text-anchor:middle;}
|
|
.term{font-family:var(--mono); font-size:12.5px; font-weight:700; fill:#2b3541;}
|
|
.w-pwr{stroke:#e03131; stroke-width:3; fill:none; stroke-linecap:round; stroke-linejoin:round;}
|
|
.w-uart{stroke:#1971c2; stroke-width:3; fill:none; stroke-linecap:round; stroke-linejoin:round;}
|
|
.w-gnd{stroke:#343a40; stroke-width:3; fill:none; stroke-linecap:round; stroke-linejoin:round;}
|
|
.w-opt{stroke:#868e96; stroke-width:2.5; fill:none; stroke-dasharray:6 4; stroke-linecap:round;}
|
|
.wlbl{font-family:var(--sans); font-size:12px; font-weight:700; fill:#2b3541;}
|
|
.jn{fill:#212529;}
|
|
.sch-note{color:var(--muted); font-size:12.5px; text-align:center; margin:12px 0 0;}
|
|
|
|
/* ---------- Callouts ---------- */
|
|
.warn{
|
|
background:var(--warnbg); border:1.5px solid #ffc9c9; border-left:6px solid var(--warn);
|
|
border-radius:12px; padding:16px 18px; margin:0 0 16px; display:flex; gap:14px; align-items:flex-start;
|
|
}
|
|
.warn .ic{flex:0 0 auto; width:34px; height:34px;}
|
|
.warn h3{margin:0 0 4px; color:#c92a2a; font-size:16px; font-weight:800;}
|
|
.warn p{margin:0; font-size:14px; color:#5a2020;}
|
|
.warn b{color:#c92a2a;}
|
|
ul.notes{margin:6px 0 0; padding:0; list-style:none;}
|
|
ul.notes li{position:relative; padding:7px 0 7px 24px; border-bottom:1px dashed var(--line); font-size:14px;}
|
|
ul.notes li:last-child{border-bottom:none;}
|
|
ul.notes li::before{content:""; position:absolute; left:4px; top:14px; width:8px; height:8px;
|
|
background:var(--accent); border-radius:2px; transform:rotate(45deg);}
|
|
code, .kbd{font-family:var(--mono); background:#eef2f6; color:#0b3d2e; padding:1px 6px; border-radius:5px; font-size:13px;}
|
|
.cmd{
|
|
font-family:var(--mono); background:#12211b; color:#c8f7d8; border-radius:10px;
|
|
padding:12px 14px; font-size:13.5px; margin:12px 0 0; overflow-x:auto;
|
|
}
|
|
.cmd .cm{color:#7fd6a3;} .cmd .ok{color:#ffd43b;}
|
|
|
|
/* ---------- Checklist ---------- */
|
|
ol.check{margin:0; padding:0; list-style:none; counter-reset:ck;}
|
|
ol.check li{
|
|
counter-increment:ck; position:relative; display:flex; align-items:flex-start; gap:12px;
|
|
padding:11px 4px 11px 0; border-bottom:1px solid var(--line); font-size:14px;
|
|
}
|
|
ol.check li:last-child{border-bottom:none;}
|
|
ol.check li .num{
|
|
flex:0 0 auto; width:24px; height:24px; border-radius:50%; background:#e7eef5; color:#1f5f97;
|
|
font-weight:800; font-size:12.5px; display:flex; align-items:center; justify-content:center;
|
|
}
|
|
ol.check li .num::before{content:counter(ck);}
|
|
ol.check li .cbox{flex:0 0 auto; width:18px; height:18px; border:2px solid #9aa6b1; border-radius:4px; margin-top:1px;}
|
|
ol.check li .txt{flex:1;}
|
|
ol.check li .txt b{color:var(--warn);}
|
|
|
|
footer{margin-top:26px; text-align:center; color:var(--muted); font-size:12px;}
|
|
|
|
/* ---------- Print ---------- */
|
|
@media print{
|
|
body{background:#fff;}
|
|
.wrap{max-width:none; padding:0;}
|
|
.hero{background:#fff !important; color:#111; border:2px solid #111; box-shadow:none;}
|
|
.hero .eyebrow{color:#444;} .hero p.sum{color:#222;}
|
|
.chip{background:#f2f2f2; color:#111; border:1px solid #999;}
|
|
.chip b{color:#000;}
|
|
h2 .n{background:#111;}
|
|
section{box-shadow:none; border:1px solid #999; break-inside:avoid; page-break-inside:avoid;}
|
|
table, tr, td, th, .warn, .card{break-inside:avoid; page-break-inside:avoid;}
|
|
.svg-pin, .svg-sch{max-width:100%;}
|
|
.cmd{background:#f2f2f2; color:#111; border:1px solid #999;}
|
|
a{color:#000;}
|
|
}
|
|
@media (max-width:560px){
|
|
.hero h1{font-size:22px;} h2{font-size:17px;}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
|
|
<!-- ============ 1. HEADER ============ -->
|
|
<header class="hero">
|
|
<p class="eyebrow">Field Installation Doc · Wiring & Pinmap</p>
|
|
<h1>Particulate-Matter Node — Site (Pi-C, sensor_id=3)</h1>
|
|
<p class="sum">
|
|
A measurement node connecting the Plantower PMS7003 laser PM sensor to the Raspberry Pi over the primary UART (9600 8N1).
|
|
The sensor co-locates on an existing leak node and reports PM1.0/PM2.5/PM10 (ug/m3) every <b>10 minutes</b> under the same
|
|
<code>sensor_id</code> as that node's leak sensor; high-PM2.5/PM10 threshold decisions and SMS dispatch are handled by the server.
|
|
</p>
|
|
<div class="chips">
|
|
<span class="chip">Raspberry Pi 4 Model B</span>
|
|
<span class="chip mono">BCM2711</span>
|
|
<span class="chip">Raspberry Pi OS Bookworm 64-bit</span>
|
|
<span class="chip">Plantower PMS7003</span>
|
|
<span class="chip mono">UART <b>/dev/serial0</b></span>
|
|
<span class="chip mono">9600 8N1</span>
|
|
<span class="chip mono">metric_type=<b>pms7003</b></span>
|
|
<span class="chip">Report interval 10 min</span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- ============ 2. PINMAP SVG ============ -->
|
|
<section>
|
|
<h2><span class="n">2</span>40-Pin Header Pinmap Diagram <small>Used pins highlighted · others in gray</small></h2>
|
|
|
|
<svg class="svg-pin" viewBox="0 0 520 672" role="img" aria-label="Raspberry Pi 40-pin GPIO header pinmap">
|
|
<!-- caption -->
|
|
<text class="cap-svg" x="260" y="20">40-pin GPIO header (J8) · physical pin numbers</text>
|
|
<!-- rails -->
|
|
<rect class="rail" x="186" y="46" width="28" height="600" rx="14"/>
|
|
<rect class="rail" x="306" y="46" width="28" height="600" rx="14"/>
|
|
|
|
<!-- Row 1 : pin1 (square = pin1 marker) / pin2 (5V power = used) -->
|
|
<rect class="p-off" x="188" y="48" width="24" height="24" rx="5"/>
|
|
<text class="pnum pnum-d" x="200" y="60">1</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="60">3V3</text>
|
|
<circle class="p-pwr" cx="320" cy="60" r="12"/>
|
|
<text class="pnum pnum-l" x="320" y="60">2</text>
|
|
<text class="plbl lbl-r lbl-used" x="344" y="60">5V (VCC)</text>
|
|
|
|
<!-- Row 2 : pin3 / pin4 -->
|
|
<circle class="p-off" cx="200" cy="90" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="90">3</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="90">GPIO2 (SDA1)</text>
|
|
<circle class="p-off" cx="320" cy="90" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="90">4</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="90">5V</text>
|
|
|
|
<!-- Row 3 : pin5 / pin6 (GND = used) -->
|
|
<circle class="p-off" cx="200" cy="120" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="120">5</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="120">GPIO3 (SCL1)</text>
|
|
<circle class="p-gnd" cx="320" cy="120" r="12"/>
|
|
<text class="pnum pnum-l" x="320" y="120">6</text>
|
|
<text class="plbl lbl-r lbl-used" x="344" y="120">GND</text>
|
|
|
|
<!-- Row 4 : pin7 / pin8 (GPIO14 TXD = optional) -->
|
|
<circle class="p-off" cx="200" cy="150" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="150">7</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="150">GPIO4</text>
|
|
<circle class="p-opt" cx="320" cy="150" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="150">8</text>
|
|
<text class="plbl lbl-r lbl-alt" x="344" y="150">GPIO14 (TXD)</text>
|
|
|
|
<!-- Row 5 : pin9 / pin10 (GPIO15 RXD = used, sensor data) -->
|
|
<circle class="p-off" cx="200" cy="180" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="180">9</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="180">GND</text>
|
|
<circle class="p-uart" cx="320" cy="180" r="12"/>
|
|
<text class="pnum pnum-l" x="320" y="180">10</text>
|
|
<text class="plbl lbl-r lbl-used" x="344" y="180">GPIO15 (RXD)</text>
|
|
|
|
<!-- Row 6 : pin11 / pin12 -->
|
|
<circle class="p-off" cx="200" cy="210" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="210">11</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="210">GPIO17</text>
|
|
<circle class="p-off" cx="320" cy="210" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="210">12</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="210">GPIO18</text>
|
|
|
|
<!-- Row 7 : pin13 / pin14 -->
|
|
<circle class="p-off" cx="200" cy="240" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="240">13</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="240">GPIO27</text>
|
|
<circle class="p-off" cx="320" cy="240" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="240">14</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="240">GND</text>
|
|
|
|
<!-- Row 8 : pin15 / pin16 -->
|
|
<circle class="p-off" cx="200" cy="270" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="270">15</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="270">GPIO22</text>
|
|
<circle class="p-off" cx="320" cy="270" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="270">16</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="270">GPIO23</text>
|
|
|
|
<!-- Row 9 : pin17 / pin18 -->
|
|
<circle class="p-off" cx="200" cy="300" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="300">17</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="300">3V3</text>
|
|
<circle class="p-off" cx="320" cy="300" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="300">18</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="300">GPIO24</text>
|
|
|
|
<!-- Row 10 : pin19 / pin20 -->
|
|
<circle class="p-off" cx="200" cy="330" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="330">19</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="330">GPIO10 (MOSI)</text>
|
|
<circle class="p-off" cx="320" cy="330" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="330">20</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="330">GND</text>
|
|
|
|
<!-- Row 11 : pin21 / pin22 -->
|
|
<circle class="p-off" cx="200" cy="360" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="360">21</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="360">GPIO9 (MISO)</text>
|
|
<circle class="p-off" cx="320" cy="360" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="360">22</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="360">GPIO25</text>
|
|
|
|
<!-- Row 12 : pin23 / pin24 -->
|
|
<circle class="p-off" cx="200" cy="390" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="390">23</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="390">GPIO11 (SCLK)</text>
|
|
<circle class="p-off" cx="320" cy="390" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="390">24</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="390">GPIO8 (CE0)</text>
|
|
|
|
<!-- Row 13 : pin25 / pin26 -->
|
|
<circle class="p-off" cx="200" cy="420" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="420">25</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="420">GND</text>
|
|
<circle class="p-off" cx="320" cy="420" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="420">26</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="420">GPIO7 (CE1)</text>
|
|
|
|
<!-- Row 14 : pin27 / pin28 -->
|
|
<circle class="p-off" cx="200" cy="450" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="450">27</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="450">GPIO0 (ID_SD)</text>
|
|
<circle class="p-off" cx="320" cy="450" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="450">28</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="450">GPIO1 (ID_SC)</text>
|
|
|
|
<!-- Row 15 : pin29 / pin30 -->
|
|
<circle class="p-off" cx="200" cy="480" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="480">29</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="480">GPIO5</text>
|
|
<circle class="p-off" cx="320" cy="480" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="480">30</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="480">GND</text>
|
|
|
|
<!-- Row 16 : pin31 / pin32 -->
|
|
<circle class="p-off" cx="200" cy="510" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="510">31</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="510">GPIO6</text>
|
|
<circle class="p-off" cx="320" cy="510" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="510">32</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="510">GPIO12</text>
|
|
|
|
<!-- Row 17 : pin33 / pin34 -->
|
|
<circle class="p-off" cx="200" cy="540" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="540">33</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="540">GPIO13</text>
|
|
<circle class="p-off" cx="320" cy="540" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="540">34</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="540">GND</text>
|
|
|
|
<!-- Row 18 : pin35 / pin36 -->
|
|
<circle class="p-off" cx="200" cy="570" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="570">35</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="570">GPIO19</text>
|
|
<circle class="p-off" cx="320" cy="570" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="570">36</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="570">GPIO16</text>
|
|
|
|
<!-- Row 19 : pin37 / pin38 -->
|
|
<circle class="p-off" cx="200" cy="600" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="600">37</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="600">GPIO26</text>
|
|
<circle class="p-off" cx="320" cy="600" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="600">38</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="600">GPIO20</text>
|
|
|
|
<!-- Row 20 : pin39 / pin40 -->
|
|
<circle class="p-off" cx="200" cy="630" r="12"/>
|
|
<text class="pnum pnum-d" x="200" y="630">39</text>
|
|
<text class="plbl lbl-l lbl-off" x="176" y="630">GND</text>
|
|
<circle class="p-off" cx="320" cy="630" r="12"/>
|
|
<text class="pnum pnum-d" x="320" y="630">40</text>
|
|
<text class="plbl lbl-r lbl-off" x="344" y="630">GPIO21</text>
|
|
|
|
<!-- pin1 hint -->
|
|
<text class="cap-svg" x="260" y="662" style="font-size:11px;">[#] square marker = pin 1 (relative to board orientation)</text>
|
|
</svg>
|
|
|
|
<div class="legend">
|
|
<span><i class="sw pwr"></i>5V power — pin 2</span>
|
|
<span><i class="sw uart"></i>UART RXD (sensor data) — pin 10</span>
|
|
<span><i class="sw gnd"></i>GND — pin 6</span>
|
|
<span><i class="sw opt"></i>UART TXD (optional) — pin 8</span>
|
|
<span><i class="sw off"></i>Unused pins</span>
|
|
</div>
|
|
<p class="diagram-cap">The pin numbers and signals in this diagram match the standard Raspberry Pi 4 Model B (BCM2711) 40-pin header layout.</p>
|
|
</section>
|
|
|
|
<!-- ============ 3. PINMAP TABLE ============ -->
|
|
<section>
|
|
<h2><span class="n">3</span>Pinmap Table <small>PMS7003 → 40-pin header</small></h2>
|
|
<table>
|
|
<thead>
|
|
<tr><th style="width:30%">PMS7003 signal</th><th style="width:24%">Direction</th><th style="width:14%">Phys pin</th><th>Notes</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><b>VCC</b> (power)</td>
|
|
<td class="mono">Pi → sensor</td>
|
|
<td><span class="pinbadge pwr">2</span></td>
|
|
<td><span class="warn-inline">* 5V required</span> — the PMS7003 fan + laser need 5V (pins 2·4)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>GND</b></td>
|
|
<td class="mono">common</td>
|
|
<td><span class="pinbadge gnd">6</span></td>
|
|
<td>Any GND works: <span class="mono">6 · 9 · 14 · 20 · 25 · 30 · 34 · 39</span></td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>TXD</b> (sensor data out)</td>
|
|
<td class="mono">sensor → Pi RXD</td>
|
|
<td><span class="pinbadge">10</span></td>
|
|
<td>GPIO15 (RXD). 3.3V logic — connects <b>directly</b>, no level shifter</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>RXD</b> (command in)</td>
|
|
<td class="mono">Pi TXD → sensor</td>
|
|
<td><span class="pinbadge na">8</span></td>
|
|
<td><b>Optional</b> — only for passive-mode/sleep commands. Active streaming needs only TXD</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>SET / RESET</b></td>
|
|
<td class="mono">—</td>
|
|
<td><span class="pinbadge na">n/c</span></td>
|
|
<td>Optional. Leave floating — internal pull-up keeps the sensor enabled/running</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<!-- ============ 4. SCHEMATIC SVG ============ -->
|
|
<section>
|
|
<h2><span class="n">4</span>Wiring Schematic <small>sensor ↔ header</small></h2>
|
|
|
|
<svg class="svg-sch" viewBox="0 0 840 380" role="img" aria-label="PMS7003 wiring schematic">
|
|
<!-- component boxes -->
|
|
<rect class="box" x="70" y="90" width="160" height="250" rx="8"/>
|
|
<rect class="box" x="640" y="90" width="150" height="250" rx="8"/>
|
|
<text class="boxtitle" x="150" y="116">PMS7003 sensor</text>
|
|
<text class="boxtitle" x="715" y="116">Pi 40-pin header</text>
|
|
|
|
<!-- PMS7003 terminal labels (right side) -->
|
|
<text class="term" x="220" y="150" text-anchor="end">VCC</text>
|
|
<text class="term" x="220" y="205" text-anchor="end">GND</text>
|
|
<text class="term" x="220" y="260" text-anchor="end">TXD</text>
|
|
<text class="term" x="220" y="315" text-anchor="end">RXD</text>
|
|
|
|
<!-- Pi terminal labels (left side) -->
|
|
<text class="term" x="650" y="150">5V (pin 2)</text>
|
|
<text class="term" x="650" y="205">GND (pin 6)</text>
|
|
<text class="term" x="650" y="260">RXD (pin 10, GPIO15)</text>
|
|
<text class="term" x="650" y="315">TXD (pin 8, GPIO14)</text>
|
|
|
|
<!-- POWER wire -->
|
|
<path class="w-pwr" d="M230 150 H640"/>
|
|
<text class="wlbl" x="330" y="140" fill="#b02a2a">Power 5V</text>
|
|
|
|
<!-- GND wire -->
|
|
<path class="w-gnd" d="M230 205 H640"/>
|
|
<text class="wlbl" x="330" y="195" fill="#2b3541">Ground GND</text>
|
|
|
|
<!-- TXD (sensor) -> RXD (Pi) : the sensor data line -->
|
|
<path class="w-uart" d="M230 260 H640"/>
|
|
<text class="wlbl" x="300" y="250" fill="#155a9c">sensor TXD → Pi RXD (GPIO15)</text>
|
|
|
|
<!-- RXD (sensor) <- TXD (Pi) : optional command line -->
|
|
<path class="w-opt" d="M230 315 H640"/>
|
|
<text class="wlbl" x="300" y="333" fill="#5c6570">Pi TXD → sensor RXD (optional)</text>
|
|
</svg>
|
|
|
|
<p class="sch-note">
|
|
Active streaming mode uses only <b>VCC · GND · sensor TXD → Pi RXD</b>. The dashed Pi TXD → sensor RXD
|
|
line is optional (needed only to send passive-mode / sleep commands). The PMS7003 TXD is 3.3V logic,
|
|
so it wires <b>directly</b> to the Pi — no level shifter.
|
|
</p>
|
|
</section>
|
|
|
|
<!-- ============ 5. ELECTRICAL NOTES ============ -->
|
|
<section>
|
|
<h2><span class="n">5</span>Electrical Design · Cautions</h2>
|
|
|
|
<div class="warn">
|
|
<svg class="ic" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M12 2 L23 21 H1 Z" fill="#ffe3e3" stroke="#e03131" stroke-width="1.6" stroke-linejoin="round"/>
|
|
<line x1="12" y1="9" x2="12" y2="15" stroke="#e03131" stroke-width="2.2" stroke-linecap="round"/>
|
|
<circle cx="12" cy="18" r="1.4" fill="#e03131"/>
|
|
</svg>
|
|
<div>
|
|
<h3>Critical — power VCC from 5V, but NEVER wire the sensor TXD to a Pi 5V pin</h3>
|
|
<p>The PMS7003 <b>VCC must be 5V (pin 2 or 4)</b> — its fan and laser will not run on 3.3V. However the
|
|
UART data lines are <b>3.3V logic</b>: the sensor's TXD connects <b>directly</b> to the Pi RXD (pin 10).
|
|
<b>Do not connect any 5V line to a GPIO</b> — that would damage the 3.3V-only Pi GPIO. Double-check pin numbers before power-up.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="notes">
|
|
<li><b>Enable the UART:</b> add <code>enable_uart=1</code> to <code>/boot/firmware/config.txt</code> (Bookworm) or use <code>raspi-config</code> → Interface Options → Serial Port.</li>
|
|
<li><b>Disable the serial login console:</b> answer "No" to the login-shell question in <code>raspi-config</code>, or remove <code>console=serial0,115200</code> from <code>cmdline.txt</code>. Otherwise the console fights the sensor for <code>/dev/serial0</code>. <code>install.sh</code> does both automatically.</li>
|
|
<li><b>Port:</b> the module reads <code>/dev/serial0</code> (alias to the primary UART; also <code>/dev/ttyS0</code> / <code>/dev/ttyAMA0</code>). 9600 baud, 8 data bits, no parity, 1 stop bit.</li>
|
|
<li><b>dialout group:</b> the service user needs the <code>dialout</code> group to open <code>/dev/serial0</code> (<code>systemd</code> unit sets <code>SupplementaryGroups=dialout</code>).</li>
|
|
<li><b>Warm-up:</b> the PMS7003 needs a few seconds of fan/laser warm-up after power-on for stable readings; the driver simply reads until a valid checksummed frame arrives.</li>
|
|
<li><b>Role split:</b> this node only reports PM1.0/PM2.5/PM10 every 10 minutes. High-PM2.5/PM10 <b>threshold decisions and SMS dispatch are done by the server</b>.</li>
|
|
<li><b>Config file:</b> <code>raspberry_pi4_pms7003/config.py</code> (<code>UART_PORT=/dev/serial0</code>, <code>UART_BAUD=9600</code>). Per-device values (SENSOR_ID 1 vs 3) are overridden in <code>config_local.py</code>.</li>
|
|
</ul>
|
|
|
|
<div class="cmd">
|
|
<span class="cm"># Verify the UART device exists (symlink to ttyS0 / ttyAMA0)</span>
|
|
$ ls -l /dev/serial0
|
|
lrwxrwxrwx 1 root root 5 ... /dev/serial0 -> <span class="ok">ttyS0</span>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ============ 6. INSTALL CHECKLIST ============ -->
|
|
<section>
|
|
<h2><span class="n">6</span>Installation Checklist</h2>
|
|
<ol class="check">
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Do wiring work with <b>power disconnected</b> (no hot-plugging)</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Connect VCC → physical pin 2 (5V) — the sensor fan/laser need <b>5V</b></span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Connect GND → physical pin 6 (or any other GND pin)</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Connect sensor <b>TXD → Pi RXD (pin 10, GPIO15)</b> — this is the data line</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">(Optional) Pi TXD (pin 8) → sensor RXD, only if sending passive-mode commands</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Leave SET/RESET floating (sensor stays enabled/running)</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Re-check all wiring — confirm <b>no 5V line touches a GPIO</b> — then power up the Pi</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Enable UART (<code>enable_uart=1</code>) and <b>disable the serial console</b> (raspi-config / install.sh), then reboot</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Run <code>ls -l /dev/serial0</code> → confirm it symlinks to <code>ttyS0</code>/<code>ttyAMA0</code></span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Verify UART_PORT / UART_BAUD in config.py, and SENSOR_ID (1 or 3) in config_local.py</span></li>
|
|
<li><span class="num"></span><span class="cbox"></span><span class="txt">Check measurement log and server reception (10-min PM reporting OK)</span></li>
|
|
</ol>
|
|
</section>
|
|
|
|
<footer>
|
|
Particulate-Matter Node · PMS7003 UART wiring doc (co-located on a leak node, sensor_id 1 or 3) — for on-site printing
|
|
</footer>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|