Files
seedbox/tools/wiring.html
Gee 2196633ed5
Some checks failed
check / check (push) Has been cancelled
Seedbox v0.1 + firmware, generic kit/synth, ROOT/SCALE pages
First commit of the repo. Contents beyond the v0.1 simulator and layout:

- Full Pico 2 W firmware (firmware/app, drivers, engine port, host test).
- BANK page and 404 mode A/B removed; learned pads play exactly what was learned.
- 404/MiniFreak renamed to KIT/SYNTH throughout (jacks, code, sim, docs,
  layout labels; wiring rebuilt).
- ROLL returns to page select after a re-roll; a freshly learned track turns on.
- LCD contrast 66.
- ROOT and SCALE pages: 21 scales, AUTO = the seed picks; global and sticky.

Intended music change: the scale table grew from 8 to 21 and AUTO picks from
all of them, so every seed's scale changed. Vectors regenerated with
npm run vectors, plus new key-override cases. Flash record is now version 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:39:36 +03:00

501 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<title>Seedbox Wiring</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=Martian+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
/* Layout: one column build guide. Board drawings first (component side, then copper side for cuts), then wire list, then panel wiring. */
:root{
--bg:#E8ECEF; --surface:#F6F7F9; --ink:#18202A; --muted:#566271; --line:#C3CBD3; --accent:#1E4C9E; --warn:#B4441F;
--board:#D9C79E; --board-edge:#B8A57A; --copper:#C98A4B; --hole:#5B4A30; --part:#2C3238; --part-ink:#E8ECEF;
--w-red:#D7263D; --w-black:#1E1E1E; --w-yellow:#D9A600; --w-green:#2E9E5B; --w-blue:#2F6FDB; --link:#7A8794;
--f-ui:'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
--f-mono:'Martian Mono', ui-monospace, Menlo, Consolas, monospace;
}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){
--bg:#12161B; --surface:#1A1F25; --ink:#E2E7EC; --muted:#8C98A5; --line:#333B44; --accent:#8EB0F2; --warn:#F07B52;
--board:#6B5D40; --board-edge:#4F4430; --copper:#B97A3E; --hole:#231C10; --part:#0E1114; --part-ink:#D9E0E6;
--w-black:#8E98A3; --link:#9AA6B2; color-scheme:dark } }
:root[data-theme="dark"]{
--bg:#12161B; --surface:#1A1F25; --ink:#E2E7EC; --muted:#8C98A5; --line:#333B44; --accent:#8EB0F2; --warn:#F07B52;
--board:#6B5D40; --board-edge:#4F4430; --copper:#B97A3E; --hole:#231C10; --part:#0E1114; --part-ink:#D9E0E6;
--w-black:#8E98A3; --link:#9AA6B2; color-scheme:dark }
*{box-sizing:border-box}
body{background:var(--bg);color:var(--ink);font:15px/1.55 var(--f-ui);padding-block:28px 56px;padding-inline:16px}
.wrap{max-width:1040px;margin:0 auto;display:grid;gap:26px}
h1{font:700 24px/1.15 var(--f-mono);margin:0}
h2{font:700 13px var(--f-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--accent);margin:0}
h3{font:700 15px var(--f-ui);margin:0}
.sub{color:var(--muted);margin:6px 0 0;max-width:74ch}
section{display:grid;gap:12px}
.card{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:14px 16px;display:grid;gap:10px;min-width:0}
.legend{display:flex;flex-wrap:wrap;gap:8px 18px;font:500 13px var(--f-ui)}
.legend span{display:inline-flex;align-items:center;gap:8px}
.legend i{display:inline-block;width:26px;height:6px;border-radius:3px}
.draw{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:10px;overflow-x:auto}
.draw svg{display:block;width:100%;min-width:640px;height:auto}
.cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px}
.tbl{overflow-x:auto;background:var(--surface);border:1px solid var(--line);border-radius:10px}
table{width:100%;border-collapse:collapse;min-width:560px}
th{font:600 10px var(--f-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--muted);text-align:left;padding:9px 12px;border-bottom:1px solid var(--line)}
td{padding:8px 12px;border-bottom:1px solid var(--line);font-size:14px;vertical-align:top}
tr:last-child td{border-bottom:0}
td.m{font:500 13px var(--f-mono);white-space:nowrap;font-variant-numeric:tabular-nums}
.sw{display:inline-block;width:12px;height:12px;border-radius:3px;vertical-align:-1px;margin-right:6px}
ol,ul{margin:0;padding-left:20px;display:grid;gap:5px}
li{max-width:78ch}
.note{color:var(--muted);font-size:13px;margin:0;max-width:78ch}
.warn{color:var(--warn);font-weight:600}
code{font:500 12.5px var(--f-mono)}
table.check td{padding:7px 12px}
table.check td.hole{font:700 18px var(--f-mono);color:var(--accent)}
table.check td.part{font-weight:600;white-space:nowrap}
table.check tr.grp td{border-top:2px solid var(--line)}
table.check tr.done td{opacity:.45}
table.check tr.done td.hole{text-decoration:line-through}
table.check input{width:20px;height:20px}
#wmap{min-width:720px}
</style>
<div class="wrap">
<header>
<h1>Seedbox wiring · v0.1</h1>
<p class="sub">One stripboard carries the Pico and the MIDI circuits. Everything on the panel (screen, encoder, pots, buttons, jacks) connects to it with hookup wire. Board positions are given as strip letter + hole number, e.g. <code>X2</code> is strip X, hole 2, counted from the left with the parts facing you.</p>
</header>
<section>
<h2>Wire map · panel to board</h2>
<p class="sub">Every panel wire, where it lands. Big blue text is the board hole: strip letter, then hole number counted from the opto end (the end away from the Pico's USB).</p>
<div class="draw"><svg id="wmap" role="img" aria-label="Board with every panel wire labelled at its hole"></svg></div>
<div class="tbl"><table class="check"><thead><tr><th>Done</th><th>Part</th><th>Lug</th><th>Wire</th><th>Board hole</th></tr></thead><tbody id="wcheck"></tbody></table></div>
<p class="note">Ticks are saved in this browser. Lug drawings for each part are under Panel wiring further down.</p>
</section>
<section>
<h2>Wire colours</h2>
<div class="card">
<div class="legend">
<span><i style="background:var(--w-red)"></i>Red · 3.3 V and the MIDI OUT supply pins</span>
<span><i style="background:var(--w-black)"></i>Black · ground</span>
<span><i style="background:var(--w-yellow)"></i>Yellow · button rows, SEED, RUN</span>
<span><i style="background:var(--w-green)"></i>Green · button columns, encoder</span>
<span><i style="background:var(--w-blue)"></i>Blue · screen, pots, MIDI signals</span>
<span><i style="background:var(--link)"></i>Grey · short links on the board</span>
</div>
<p class="note">If your five spools are other colours, keep the roles and rename them. Red and black for power and ground matter most; the rest just keep bundles readable.</p>
</div>
</section>
<section>
<h2>Board · parts side</h2>
<div class="draw"><svg id="top" role="img" aria-label="Stripboard seen from the parts side"></svg></div>
<p class="note">Copper strips run left to right underneath (shown faintly). Board size 34 holes × 28 strips (about 86 × 71 mm). The Pico sits in two 20-pin female headers with pins 1 and 40 on strip A, so its USB end is flush with the top edge of the board and the micro-USB plug reaches out past it: line that edge up with a slot in the box wall. Numbered dots <b>P1</b>… (pads) are where off-board wires solder; the wire map at the top says where each one goes.</p>
</section>
<section>
<h2>Board · copper side, for cutting</h2>
<div class="draw"><svg id="bottom" role="img" aria-label="Stripboard seen from the copper side with cuts marked"></svg></div>
<div class="cols">
<div class="card"><h3>Track cuts</h3><ul id="cutlist"></ul>
<p class="note">This view is flipped: hole 1 is on the <b>right</b>. Cut with a 3–4 mm drill bit twisted by hand or a spot-face cutter, then check each cut with the meter's continuity beeper.</p></div>
<div class="card"><h3>Power strips along the bottom</h3>
<ul><li><b>Z</b> · 5 V from the Pico's VBUS (only the opto uses it)</li><li><b>AA</b> · ground</li><li><b>AB</b> · 3.3 V from the Pico's 3V3 pin</li></ul>
<p class="note">Every red wire to the panel starts on AB, every black wire on AA (or on the AGND strip for the pots).</p></div>
</div>
</section>
<section>
<h2>Board parts and links</h2>
<div class="tbl"><table><thead><tr><th>Part</th><th>Value</th><th>Holes</th><th>Note</th></tr></thead><tbody id="partlist"></tbody></table></div>
</section>
<section>
<h2>Off-board wires</h2>
<div class="tbl"><table><thead><tr><th>#</th><th>Board hole</th><th>Colour</th><th>Pico pin / signal</th><th>Goes to</th></tr></thead><tbody id="wirelist"></tbody></table></div>
</section>
<section>
<h2>Panel wiring</h2>
<div class="cols">
<div class="card" style="grid-column:1/-1"><h3>Button matrix · seen from the back</h3>
<div class="draw" style="padding:6px"><svg id="matrix" role="img" aria-label="Button matrix wiring"></svg></div>
<p class="note">Each button: one lug to its <b>row</b> wire (yellow, daisy-chained along the row), the other lug through a diode to its <b>column</b> wire (green, daisy-chained down the column). The diode's band points to the column. Solder the diodes straight onto the button lugs and sleeve them. 11 track buttons, each with its own diode, plus D1 on the board: 12 diodes. GHOST is wired at row 2 / column 4 (where FILL used to be). The RUN toggle sits in the grid's row 1 / column 4 spot but is not part of the matrix: row 1's wire stops at SNARE. <span class="warn">The drawing is from behind the panel, the side you solder on: KICK is top-right, RUN top-left.</span></p></div>
<div class="card"><h3>MIDI jacks · 3.5 mm TRS, Type A</h3>
<svg id="din" viewBox="0 0 300 150" role="img" aria-label="TRS jack lugs seen from inside the box"></svg>
<ul>
<li><b>MIDI IN</b>: ring → Y1, tip → W1. Sleeve stays empty.</li>
<li><b>OUT KIT</b>: tip → X26, ring → V22, sleeve → AA22.</li>
<li><b>OUT SYNTH</b>: tip → U31, ring → V31, sleeve → AA31.</li>
</ul>
<p class="note">Use stereo (TRS) jacks; mono ones have no ring. Lug order differs between jack models, so find the tip, ring and sleeve lugs with the meter and a plug inserted. Type A is the MIDI standard: a plain stereo 3.5 mm cable goes straight to another Type A device (e.g. SP-404MKII). A DIN device (e.g. MiniFreak) needs a TRS-to-DIN adapter that says <b>Type A</b>; a Type B one swaps tip and ring and gives silence (nothing breaks).</p></div>
<div class="card"><h3>Nokia 5110 module</h3>
<svg id="lcd" viewBox="0 0 300 92" role="img" aria-label="LCD header pins"></svg>
<ul>
<li>Pin order as printed on your module: 1 VCC, 2 GND, 3 SCE, 4 RST, 5 D/C, 6 DN (MOSI), 7 SCLK, 8 LED.</li>
<li><b>LED</b> (backlight): wire it through a 330 Ω resistor to AB22 (3.3 V); put the resistor inline at the module end and sleeve it. On boards with this pin order that lights it. If it stays dark, move that wire to any free hole on strip AA (ground). GP22 (L27) is left free: it's only needed if you ever want dimming, through a transistor.</li>
<li><b>SCE</b> can go straight to ground instead of S27 if it's the only thing on the SPI bus; that frees GP17.</li>
</ul></div>
<div class="card"><h3>Encoder module</h3>
<svg id="enc" viewBox="0 0 300 80" role="img" aria-label="Encoder module pins"></svg>
<p class="note">Pin order as printed on your module: VCC, GND, SW, SIB, SIA. Power it from 3.3 V (AB4), never 5 V: its pull-up resistors would put 5 V on the Pico's pins. If turning it clockwise steps the wrong way, swap SIA and SIB (or flip it in firmware).</p></div>
<div class="card"><h3>Pots, SEED, RUN</h3>
<svg id="misc" viewBox="0 0 300 150" role="img" aria-label="Pots, SEED and RUN wiring"></svg>
<ul>
<li>Pots, lugs facing down, shaft toward you: left lug to AGND (H27), middle to its pin, right lug to 3.3 V (AB25). Chain the left lugs together and the right lugs together.</li>
<li>SEED: one lug to Q2, the other to the ground bus (AA6).</li>
<li>RUN: middle lug to S2, one outer lug to the ground bus. If RUN and STOP come out swapped, use the other outer lug.</li>
</ul></div>
</div>
</section>
<section>
<h2>Build order and checks</h2>
<div class="cols">
<div class="card"><h3>Board</h3>
<ol>
<li>Cut the board to 34 × 28 holes. For the top edge, cut through the row of holes above strip A, then sand it back to about 1.3 mm from the strip A holes (stop before the strip A copper). That puts the Pico's edge flush with the board; unsanded, the Pico sits about 1 mm back, which still works if the wall slot lets the plug's moulding through. Make the track cuts and beep each one out.</li>
<li>Solder the two Pico headers, the DIP-8 socket (notch toward strip U), then the resistors and the diode (band on V4). Leave C1 for step 4.</li>
<li>Solder the grey links with insulated wire. The two long ones (GP0 and GP4) run straight down <b>under the Pico, between its two headers</b>: fit them before the Pico goes in.</li>
<li>Before fitting C1, with the Pico and the 6N137 out: AB to AA must <b>not</b> beep, Z to AA must not beep. Then fit C1 (it can chirp briefly on the beeper once it is in).</li>
</ol></div>
<div class="card"><h3>First power</h3>
<ol>
<li>Plug in the Pico only, power from USB. Measure AB to AA: about 3.3 V. Z to AA: about 5 V.</li>
<li>Unplug, insert the 6N137 (notch toward strip U, pin 1 at hole U5).</li>
<li>Wire the panel. Before closing the box, check that no bare lugs touch each other.</li>
<li>The box needs the Pico firmware (the C port of this simulator) before it does anything. That's the next step. Firmware note: GP0 and GP4 drive the MIDI loops directly (about 6–8 mA), above the RP2350's default 4 mA drive, so set them to 8–12 mA with <code>gpio_set_drive_strength()</code>.</li>
</ol></div>
</div>
</section>
</div>
<script>
/* Stripboard layout for Seedbox v0.1. Holes are [col,row], 0-based, seen from the COMPONENT side.
Copper strips run left to right along each row.
The Pico's pin 1 and 40 sit on the first strip (A), so its USB end is flush with the board edge. */
var LAYOUT=(function(){
var W=34, H=28;
var cuts=[];
function cut(c,r0,r1){ for(var r=r0;r<=r1;r++) cuts.push([c,r]); }
cut(15,0,19); // under the Pico: separates its left and right pin rows
cut(5,20,24); // opto: left pins / right pins, IN4 strip
cut(13,20,23); // opto area / MIDI OUT area (strip Y stays whole: it carries GP4)
cut(24,20,22); // MIDI OUT A / MIDI OUT B
cut(22,23,23); // strip X: GP0 side / KIT tip side, R4 lies across this cut
var LP=['GP0','GP1','GND','GP2','GP3','GP4','GP5','GND','GP6','GP7','GP8','GP9','GND','GP10','GP11','GP12','GP13','GND','GP14','GP15'];
var RP=['VBUS','VSYS','GND','3V3_EN','3V3','ADC_VREF','GP28','AGND','GP27','GP26','RUN','GP22','GND','GP21','GP20','GP19','GP18','GND','GP17','GP16'];
var pico={id:'pico',kind:'pico',pins:[]};
for(var i=0;i<20;i++){ pico.pins.push({h:[12,i],n:LP[i],num:i+1}); pico.pins.push({h:[19,i],n:RP[i],num:40-i}); }
var parts=[pico,
{id:'U1',kind:'dip8',label:'6N137',pins:[{h:[4,20],n:'1 NC'},{h:[4,21],n:'2 A'},{h:[4,22],n:'3 K'},{h:[4,23],n:'4 NC'},{h:[7,23],n:'5 GND'},{h:[7,22],n:'6 VO'},{h:[7,21],n:'7 VE'},{h:[7,20],n:'8 VCC'}]},
{id:'R1',kind:'res',label:'220 Ω',pins:[{h:[1,21]},{h:[1,24]}]},
{id:'D1',kind:'diode',label:'1N4148',pins:[{h:[3,21],n:'K'},{h:[3,22],n:'A'}]},
{id:'R2',kind:'res',label:'1 kΩ',pins:[{h:[9,22]},{h:[9,27]}]},
{id:'C1',kind:'cap',label:'100 nF',pins:[{h:[10,20]},{h:[10,23]}]},
{id:'R4',kind:'res',label:'10 Ω',pins:[{h:[20,23]},{h:[24,23]}]},
{id:'R5',kind:'res',label:'33 Ω ½ W',pins:[{h:[18,21]},{h:[18,27]}]},
{id:'R6',kind:'res',label:'10 Ω',pins:[{h:[27,20]},{h:[27,24]}]},
{id:'R7',kind:'res',label:'33 Ω ½ W',pins:[{h:[29,21]},{h:[29,27]}]}
];
var jumpers=[
{a:[33,0],b:[33,25],n:'5 V (VBUS) to the 5 V strip'},
{a:[31,4],b:[31,27],n:'3V3 to the 3V3 strip'},
{a:[32,17],b:[32,26],n:'GND to the GND strip'},
{a:[12,20],b:[12,25],n:'opto VCC to 5 V'},
{a:[9,20],b:[9,21],n:'opto enable (pin 7) to VCC (pin 8): the 6N137 output only works with pin 7 high'},
{a:[11,23],b:[11,26],n:'opto GND to GND'},
{a:[8,22],b:[8,1],n:'opto out to GP1 (MIDI RX), strip B'},
{a:[14,0],b:[14,23],n:'GP0 (TX KIT) to strip X (R4), straight down under the Pico between its headers'},
{a:[13,5],b:[13,24],n:'GP4 (TX SYNTH) to strip Y (R6), straight down under the Pico between its headers'}
];
/* Off-board wires. col = spool colour role. to = where the other end goes. */
var wires=[
// MIDI jacks: 3.5 mm TRS, Type A (tip = DIN pin 5, ring = pin 4, sleeve = pin 2)
{h:[0,24],col:'blue',to:'MIDI IN jack · ring (pin 4)'},
{h:[0,22],col:'blue',to:'MIDI IN jack · tip (pin 5)'},
{h:[25,23],col:'blue',to:'OUT KIT jack · tip (pin 5)'},
{h:[21,21],col:'red',to:'OUT KIT jack · ring (pin 4)'},
{h:[21,26],col:'black',to:'OUT KIT jack · sleeve (pin 2)'},
{h:[30,20],col:'blue',to:'OUT SYNTH jack · tip (pin 5)'},
{h:[30,21],col:'red',to:'OUT SYNTH jack · ring (pin 4)'},
{h:[30,26],col:'black',to:'OUT SYNTH jack · sleeve (pin 2)'},
// screen
{h:[26,13],col:'blue',to:'LCD · RST (pin 4)'},
{h:[26,18],col:'blue',to:'LCD · SCE (pin 3)'},
{h:[26,14],col:'blue',to:'LCD · D/C (pin 5)'},
{h:[26,15],col:'blue',to:'LCD · DN/MOSI (pin 6)'},
{h:[26,16],col:'blue',to:'LCD · SCLK (pin 7)'},
{h:[22,27],col:'red',to:'LCD · VCC (pin 1)'},
{h:[22,26],col:'black',to:'LCD · GND (pin 2)'},
{h:[21,27],col:'red',to:'LCD · LED (pin 8) through 330 Ω'},
// encoder
{h:[1,13],col:'green',to:'Encoder · SIA'},
{h:[1,14],col:'green',to:'Encoder · SIB'},
{h:[1,15],col:'green',to:'Encoder · SW'},
{h:[3,27],col:'red',to:'Encoder · VCC'},
{h:[3,26],col:'black',to:'Encoder · GND'},
// pots
{h:[26,9],col:'blue',to:'ENERGY pot · middle lug'},
{h:[26,8],col:'blue',to:'DRIFT pot · middle lug'},
{h:[24,27],col:'red',to:'Pots · right lugs (chain both)'},
{h:[26,7],col:'black',to:'Pots · left lugs (chain both)'},
// buttons
{h:[1,16],col:'yellow',to:'SEED · one lug'},
{h:[1,18],col:'yellow',to:'RUN toggle · middle lug'},
{h:[5,26],col:'black',to:'Panel ground bus · SEED other lug, RUN one outer lug'},
{h:[1,3],col:'yellow',to:'Matrix row 1 (KICK, KICK 2, SNARE)'},
{h:[1,4],col:'yellow',to:'Matrix row 2 (HAT, HAT 16, PERC, GHOST)'},
{h:[1,6],col:'yellow',to:'Matrix row 3 (BASS, MEL A, MEL B, PAD)'},
{h:[1,8],col:'green',to:'Matrix column 1 (KICK, HAT, BASS)'},
{h:[1,9],col:'green',to:'Matrix column 2 (KICK 2, HAT 16, MEL A)'},
{h:[1,10],col:'green',to:'Matrix column 3 (SNARE, PERC, MEL B)'},
{h:[1,11],col:'green',to:'Matrix column 4 (GHOST, PAD)'}
];
return {W:W,H:H,cuts:cuts,parts:parts,jumpers:jumpers,wires:wires};
})();
if(typeof module!=='undefined') module.exports=LAYOUT;
</script><script>(function(){
var L=LAYOUT, NS='http://www.w3.org/2000/svg';
function css(n){ return getComputedStyle(document.documentElement).getPropertyValue(n).trim(); }
function el(tag,a,p,txt){ var e=document.createElementNS(NS,tag); for(var k in a) e.setAttribute(k,a[k]); if(txt!=null) e.textContent=txt; p.appendChild(e); return e; }
function rowName(r){ return r<26 ? String.fromCharCode(65+r) : 'A'+String.fromCharCode(65+r-26); }
function pos(h){ return rowName(h[1])+(h[0]+1); }
function H(p){ return /^P\d+$/.test(p) ? pos(L.wires[+p.slice(1)-1].h) : p; }
var COL={red:'--w-red',black:'--w-black',yellow:'--w-yellow',green:'--w-green',blue:'--w-blue'};
var cutKey={}; L.cuts.forEach(function(c){ cutKey[c[0]+','+c[1]]=1; });
function strips(svg,X,C,strong){
for(var r=0;r<L.H;r++){
var start=0;
for(var c=0;c<=L.W;c++){
if(c===L.W || cutKey[c+','+r]){
if(c>start){ var x0=Math.min(X(start),X(c-1))-0.42, x1=Math.max(X(start),X(c-1))+0.42;
el('rect',{x:x0,y:r-0.36,width:x1-x0,height:0.72,rx:0.2,fill:C.copper,opacity:strong?1:0.28},svg); }
start=c+1;
}
}
}
}
function holes(svg,X,C){ for(var r=0;r<L.H;r++) for(var c=0;c<L.W;c++) el('circle',{cx:X(c),cy:r,r:0.16,fill:C.hole},svg); }
function labels(svg,X,C,flip){
var g=el('g',{'font-family':css('--f-mono'),'font-size':'0.55',fill:C.muted},svg);
for(var c=0;c<L.W;c++) if(c%2===0 || c===L.W-1){ el('text',{x:X(c),y:-1.75,'text-anchor':'middle'},g,String(c+1)); el('text',{x:X(c),y:L.H+0.9,'text-anchor':'middle'},g,String(c+1)); }
for(var r=0;r<L.H;r++){ el('text',{x:-1.3,y:r+0.2,'text-anchor':'middle'},g,rowName(r)); el('text',{x:L.W+0.3,y:r+0.2,'text-anchor':'middle'},g,rowName(r)); }
}
function frame(svg){ svg.setAttribute('viewBox','-2.4 -2.6 '+(L.W+3.8)+' '+(L.H+4.0)); }
var U1label=(L.parts.filter(function(p){ return p.id==='U1'; })[0]||{}).label||'';
function drawTop(){
var svg=document.getElementById('top'); svg.innerHTML=''; frame(svg);
var C={copper:css('--copper'),hole:css('--hole'),muted:css('--muted'),part:css('--part'),pink:css('--part-ink'),link:css('--link'),warn:css('--warn'),board:css('--board'),edge:css('--board-edge'),ink:css('--ink')};
var X=function(c){ return c; };
el('rect',{x:-0.8,y:-0.5,width:L.W+0.6,height:L.H+0.3,rx:0.4,fill:C.board,stroke:C.edge,'stroke-width':0.12},svg);
strips(svg,X,C,false); holes(svg,X,C); labels(svg,X,C);
L.cuts.forEach(function(c){ var g={stroke:C.warn,'stroke-width':0.1,opacity:0.7}; el('line',Object.assign({x1:c[0]-0.22,y1:c[1]-0.22,x2:c[0]+0.22,y2:c[1]+0.22},g),svg); el('line',Object.assign({x1:c[0]-0.22,y1:c[1]+0.22,x2:c[0]+0.22,y2:c[1]-0.22},g),svg); });
var f=css('--f-mono');
L.parts.forEach(function(p){
if(p.kind==='pico'){
el('rect',{x:11.3,y:-0.55,width:8.4,height:20.1,rx:0.4,fill:C.part,opacity:0.92},svg);
el('rect',{x:14.3,y:-1.35,width:2.4,height:1.35,rx:0.2,fill:'#9AA3AD'},svg); el('text',{x:15.5,y:-0.55,'text-anchor':'middle','font-family':f,'font-size':'0.42',fill:'#111'},svg,'USB');
el('text',{x:15.5,y:1.6,'text-anchor':'middle','font-family':f,'font-size':'0.62',fill:C.pink},svg,'PICO 2W');
p.pins.forEach(function(pp){ var left=pp.h[0]===12;
el('rect',{x:pp.h[0]-0.32,y:pp.h[1]-0.32,width:0.64,height:0.64,fill:'#111',stroke:'#D4AF37','stroke-width':0.08},svg);
el('text',{x:left?pp.h[0]+0.55:pp.h[0]-0.55,y:pp.h[1]+0.16,'text-anchor':left?'start':'end','font-family':f,'font-size':'0.42',fill:C.pink},svg,pp.n); });
} else if(p.kind==='dip8'){
el('rect',{x:4.25,y:19.55,width:2.5,height:3.9,rx:0.15,fill:C.part},svg);
el('path',{d:'M 5 19.55 a 0.5 0.5 0 0 0 1 0',fill:'#555'},svg);
el('text',{x:5.5,y:21.7,'text-anchor':'middle','font-family':f,'font-size':'0.42',fill:C.pink,transform:'rotate(90 5.5 21.6)'},svg,U1label);
p.pins.forEach(function(pp){ el('rect',{x:pp.h[0]-0.25,y:pp.h[1]-0.18,width:0.5,height:0.36,fill:'#BFC6CD'},svg); });
el('text',{x:3.55,y:20.15,'text-anchor':'end','font-family':f,'font-size':'0.4',fill:C.ink},svg,'1');
} else {
var a=p.pins[0].h, b=p.pins[1].h, horiz=a[1]===b[1], mx=(a[0]+b[0])/2, my=(a[1]+b[1])/2, len=horiz?Math.abs(b[0]-a[0]):Math.abs(b[1]-a[1]);
el('line',{x1:a[0],y1:a[1],x2:b[0],y2:b[1],stroke:'#9AA3AD','stroke-width':0.12},svg);
var bw=p.kind==='cap'?0.7:0.62, bh=Math.min(len-0.5,p.kind==='diode'?0.7:2.2);
var body=p.kind==='res'?'#C9A26B':p.kind==='diode'?'#D9663A':'#3D7BC9';
if(horiz){ el('rect',{x:mx-bh/2,y:my-bw/2,width:bh,height:bw,rx:0.25,fill:body,stroke:'#00000055','stroke-width':0.05},svg);
el('text',{x:mx,y:my-0.5,'text-anchor':'middle','font-family':f,'font-size':'0.42',fill:C.ink},svg,p.id); }
else el('rect',{x:a[0]-bw/2,y:my-bh/2,width:bw,height:bh,rx:0.25,fill:body,stroke:'#00000055','stroke-width':0.05,'stroke-dasharray':p.opt?'0.2 0.12':''},svg);
if(p.kind==='diode'){ var ky=p.pins[0].n==='K'?p.pins[0].h[1]:p.pins[1].h[1]; el('rect',{x:a[0]-bw/2,y:(ky<my?my-bh/2:my+bh/2-0.16),width:bw,height:0.16,fill:'#111'},svg); }
if(!horiz) el('text',{x:a[0]+0.48,y:my+0.15,'font-family':f,'font-size':'0.42',fill:C.ink},svg,p.id+(p.opt?'*':''));
}
p.pins.forEach(function(pp){ if(p.kind!=='pico') el('circle',{cx:pp.h[0],cy:pp.h[1],r:0.13,fill:'#D4AF37'},svg); });
});
L.jumpers.forEach(function(j){
el('line',{x1:j.a[0],y1:j.a[1],x2:j.b[0],y2:j.b[1],stroke:C.link,'stroke-width':0.2,'stroke-linecap':'round',opacity:0.9},svg);
[j.a,j.b].forEach(function(h){ el('circle',{cx:h[0],cy:h[1],r:0.2,fill:C.link},svg); });
});
L.wires.forEach(function(w,i){
var col=css(COL[w.col]);
el('circle',{cx:w.h[0],cy:w.h[1],r:0.3,fill:col,stroke:'#fff','stroke-width':0.07},svg);
var right=w.h[0]>=20, tx=w.h[0]+(right?0.42:-0.42);
el('text',{x:tx,y:w.h[1]-0.3,'text-anchor':right?'start':'end','font-family':f,'font-size':'0.48','font-weight':'700',fill:C.ink},svg,'P'+(i+1));
});
var rails=[[25,'5 V'],[26,'GND'],[27,'3V3']];
rails.forEach(function(r){ el('text',{x:L.W+1.15,y:r[0]+0.2,'font-family':f,'font-size':'0.45',fill:C.warn},svg,r[1]); });
}
function drawBottom(){
var svg=document.getElementById('bottom'); svg.innerHTML=''; frame(svg);
var C={copper:css('--copper'),hole:css('--hole'),muted:css('--muted'),warn:css('--warn'),board:css('--board'),edge:css('--board-edge'),part:css('--part')};
var X=function(c){ return L.W-1-c; };
el('rect',{x:-0.8,y:-0.5,width:L.W+0.6,height:L.H+0.3,rx:0.4,fill:C.board,stroke:C.edge,'stroke-width':0.12},svg);
strips(svg,X,C,true); holes(svg,X,C); labels(svg,X,C);
el('rect',{x:X(19)-0.5,y:-0.55,width:8.4,height:20.1,rx:0.4,fill:'none',stroke:C.muted,'stroke-width':0.08,'stroke-dasharray':'0.3 0.2'},svg);
el('text',{x:X(15.5),y:-0.85,'text-anchor':'middle','font-family':css('--f-mono'),'font-size':'0.42',fill:C.muted},svg,'Pico outline (other side), USB at this edge');
L.cuts.forEach(function(c){ var x=X(c[0]);
el('rect',{x:x-0.45,y:c[1]-0.45,width:0.9,height:0.9,rx:0.15,fill:C.board},svg);
el('circle',{cx:x,cy:c[1],r:0.34,fill:'none',stroke:C.warn,'stroke-width':0.14},svg); });
var cl=document.getElementById('cutlist'); cl.innerHTML='';
var groups={}; L.cuts.forEach(function(c){ (groups[c[0]]=groups[c[0]]||[]).push(c[1]); });
Object.keys(groups).forEach(function(k){ var rs=groups[k].sort(function(a,b){return a-b;}); var li=document.createElement('li');
li.textContent='Hole '+(+k+1)+(rs.length>1?' on strips '+rowName(rs[0])+' to '+rowName(rs[rs.length-1])+' ('+rs.length+' cuts)':' on strip '+rowName(rs[0])+' (1 cut)'); cl.appendChild(li); });
}
function tables(){
var pl=document.getElementById('partlist'); pl.innerHTML='';
function row(tb,cells,mcols){ var tr=document.createElement('tr'); cells.forEach(function(c,i){ var td=document.createElement('td'); if(mcols.indexOf(i)>=0) td.className='m'; if(c && c.nodeType) td.appendChild(c); else td.textContent=c; tr.appendChild(td); }); tb.appendChild(tr); }
var notes={U1:'6N137 in a DIP-8 socket. Notch toward strip U, pin 1 at hole U5. Pin 7 (enable) is tied to pin 8 by the U10–V10 link.',R1:'MIDI IN current limit. 3-hole span: stand it upright, or use a small ⅛ W part flat. If learning ever misses notes, use 150 Ω.',D1:'Band (cathode) on V4, the opto LED anode side. Stands upright (1-hole span).',R2:'Opto output pull-up to 3.3 V',C1:'6N137 supply bypass, right beside the chip: U11 is its 5 V (pin 8) strip, X11 its GND (pin 5) strip. Required. Fit it after the beep test.',R4:'MIDI OUT KIT, TX line (RC). Lies flat along strip X, across the cut at X23 (4-hole span, ¼ W fits). SEED hangs right above it.',R5:'MIDI OUT KIT, 3.3 V line (RA). ½ W: a mono plug in the jack shorts it to ground.',R6:'MIDI OUT SYNTH, TX line (RC)',R7:'MIDI OUT SYNTH, 3.3 V line (RA). ½ W, same reason as R5.'};
row(pl,['Pico 2W','2 × 20-pin female header','A13–T13 and A20–T20 (pins 1–20 down the left, 40–21 down the right)','USB end flush with the top edge of the board, past strip A'],[2]);
L.parts.forEach(function(p){ if(p.kind==='pico') return;
row(pl,[p.id,p.label,p.kind==='dip8'?'U5–X5, U8–X8':p.pins.map(function(pp){ return pos(pp.h); }).join(' – '),notes[p.id]||''],[2]); });
L.jumpers.forEach(function(j){ row(pl,['Link','wire',pos(j.a)+' – '+pos(j.b),j.n],[2]); });
var picoAt={}; L.parts[0].pins.forEach(function(pp){ picoAt[pp.h[1]+(pp.h[0]===12?'L':'R')]=pp.n; });
function pinFor(h){ var r=h[1]; if(r>=0 && r<=19) return picoAt[r+(h[0]<15?'L':'R')]; if(r===25) return '5 V strip'; if(r===26) return 'GND strip'; if(r===27) return '3V3 strip';
return {'0,24':'via R1 to opto','0,22':'opto cathode','25,23':'via R4 (10 Ω) from GP0','21,21':'via R5 (33 Ω) from 3V3','30,20':'via R6 (10 Ω) from GP4','30,21':'via R7 (33 Ω) from 3V3'}[h[0]+','+h[1]]||''; }
var wl=document.getElementById('wirelist'); wl.innerHTML='';
L.wires.forEach(function(w,i){ var s=document.createElement('span'); var sw=document.createElement('span'); sw.className='sw'; sw.style.background='var('+COL[w.col]+')'; s.appendChild(sw); s.appendChild(document.createTextNode(w.col));
row(wl,['P'+(i+1),pos(w.h),s,pinFor(w.h),w.to],[0,1]); });
}
function drawMatrix(){ // drawn from the BACK (lug side), so the columns are mirrored: col 4 on the left, KICK top-right
var svg=document.getElementById('matrix'); svg.innerHTML=''; svg.setAttribute('viewBox','0 0 700 330');
var C={y:css('--w-yellow'),g:css('--w-green'),ink:css('--ink'),muted:css('--muted'),part:css('--part'),line:css('--line')}, f=css('--f-mono');
var names=[['KICK','KICK 2','SNARE',null],['HAT','HAT 16','PERC','GHOST'],['BASS','MEL A','MEL B','PAD']];
var x0=110, dx=118, y0=84, dy=80, rowEnd=560;
function bxOf(c){ return x0+(3-c)*dx; }
for(var c=0;c<4;c++){ var cx=bxOf(c)-38; el('line',{x1:cx,y1:c===3?y0+dy-28:y0-10,x2:cx,y2:y0+2*dy+40,stroke:C.g,'stroke-width':3},svg);
el('text',{x:cx,y:y0+2*dy+60,'text-anchor':'middle','font-family':f,'font-size':12,'font-weight':700,fill:C.ink},svg,'Col '+(c+1)+' → '+H('P'+(32+c))); }
for(var r=0;r<3;r++){ var ry=y0+r*dy-18, left=bxOf(r===0?2:3)+9;
el('line',{x1:left,y1:ry,x2:rowEnd,y2:ry,stroke:C.y,'stroke-width':3},svg);
el('text',{x:rowEnd+6,y:ry+4,'font-family':f,'font-size':12,'font-weight':700,fill:C.ink},svg,'Row '+(r+1)+' → '+H('P'+(29+r)));
for(c=0;c<4;c++){ var bx=bxOf(c), by=y0+r*dy;
if(!names[r][c]){ el('rect',{x:bx-12,y:by-16,width:24,height:32,rx:4,fill:'none',stroke:C.muted,'stroke-width':1.2,'stroke-dasharray':'4 3'},svg);
el('text',{x:bx,y:by+4,'text-anchor':'middle','font-family':f,'font-size':10,fill:C.ink},svg,'RUN');
el('text',{x:bx,y:by+34,'text-anchor':'middle','font-family':f,'font-size':9,fill:C.muted},svg,'toggle, not in matrix'); continue; }
el('circle',{cx:bx,cy:by,r:17,fill:C.part},svg);
el('text',{x:bx,y:by+34,'text-anchor':'middle','font-family':f,'font-size':11,fill:C.muted},svg,names[r][c]);
var lRow=bx+9, lDio=bx-9, colX=bx-38;
el('circle',{cx:lRow,cy:by,r:3.2,fill:'#D4AF37'},svg); el('circle',{cx:lDio,cy:by,r:3.2,fill:'#D4AF37'},svg);
el('line',{x1:lRow,y1:by-3,x2:lRow,y2:ry,stroke:C.y,'stroke-width':2},svg);
el('line',{x1:lDio,y1:by,x2:colX,y2:by,stroke:'#9AA3AD','stroke-width':1.5},svg);
el('rect',{x:colX+4,y:by-4,width:18,height:8,rx:2,fill:'#D9663A'},svg); el('rect',{x:colX+4,y:by-4,width:4,height:8,fill:'#111'},svg);
}
}
el('text',{x:x0-60,y:22,'font-family':f,'font-size':12,'font-weight':700,fill:C.ink},svg,'Seen from the BACK (lug side): KICK is top-right.');
el('text',{x:x0-60,y:38,'font-family':f,'font-size':10,fill:C.muted},svg,'Diode band → column wire. Lid flipped left to right, back wall still at the top.');
}
function drawDin(){ // 3.5 mm TRS panel jacks, Type A, lugs seen from inside the box
var svg=document.getElementById('din'); svg.innerHTML='';
var f=css('--f-mono'), ink=css('--ink'), m=css('--muted');
var jacks=[{n:'IN',x:52,w:{T:['--w-blue','P2'],R:['--w-blue','P1']}},{n:'OUT KIT',x:150,w:{T:['--w-blue','P3'],R:['--w-red','P4'],S:['--w-black','P5']}},{n:'OUT SYNTH',x:248,w:{T:['--w-blue','P6'],R:['--w-red','P7'],S:['--w-black','P8']}}];
var LUG={T:[-20,'T·5'],R:[0,'R·4'],S:[20,'S·2']};
jacks.forEach(function(j){
el('rect',{x:j.x-30,y:30,width:60,height:34,rx:4,fill:'#2C3238'},svg);
el('circle',{cx:j.x,cy:47,r:9,fill:'none',stroke:'#9AA3AD','stroke-width':2},svg);
Object.keys(LUG).forEach(function(k){ var lx=j.x+LUG[k][0], used=j.w[k];
el('rect',{x:lx-3,y:64,width:6,height:12,fill:'#C9CFD5'},svg);
if(used){ el('line',{x1:lx,y1:76,x2:lx,y2:100,stroke:css(used[0]),'stroke-width':4,'stroke-linecap':'round'},svg);
el('text',{x:lx,y:112,'text-anchor':'middle','font-family':f,'font-size':11,'font-weight':700,fill:ink},svg,H(used[1])); }
else el('text',{x:lx,y:90,'text-anchor':'middle','font-family':f,'font-size':8,fill:m},svg,'—');
el('text',{x:lx,y:124,'text-anchor':'middle','font-family':f,'font-size':8,fill:m},svg,LUG[k][1]); });
el('text',{x:j.x,y:142,'text-anchor':'middle','font-family':f,'font-size':11,fill:ink},svg,j.n); });
el('text',{x:150,y:14,'text-anchor':'middle','font-family':f,'font-size':9,fill:m},svg,'Type A · T tip = pin 5 · R ring = 4 · S sleeve = 2');
}
function pinRow(id,pins,w){
var svg=document.getElementById(id); svg.innerHTML=''; var f=css('--f-mono'), ink=css('--ink');
var n=pins.length, sp=Math.min(36,260/n), x0=150-sp*(n-1)/2;
pins.forEach(function(p,i){ var x=x0+i*sp;
el('rect',{x:x-6,y:14,width:12,height:12,fill:'#111',stroke:'#D4AF37','stroke-width':1.5},svg);
el('line',{x1:x,y1:26,x2:x,y2:52,stroke:p[1]?css(p[1]):'none','stroke-width':4,'stroke-linecap':'round'},svg);
el('text',{x:x,y:10,'text-anchor':'middle','font-family':f,'font-size':n>6?8:9,fill:ink},svg,p[0]);
el('text',{x:x,y:68,'text-anchor':'middle','font-family':f,'font-size':10,'font-weight':700,fill:ink},svg,H(p[2]||'')); });
}
function drawMisc(){
var svg=document.getElementById('misc'); svg.innerHTML=''; var f=css('--f-mono'), ink=css('--ink'), m=css('--muted');
function pot(x,label,wp){ el('circle',{cx:x,cy:42,r:20,fill:'#2C3238'},svg); el('text',{x:x,y:14,'text-anchor':'middle','font-family':f,'font-size':10,fill:ink},svg,label);
[[-12,'--w-black','P25'],[0,'--w-blue',wp],[12,'--w-red','P24']].forEach(function(l){ el('rect',{x:x+l[0]-2.5,y:62,width:5,height:12,fill:'#C9CFD5'},svg); el('line',{x1:x+l[0],y1:74,x2:x+l[0],y2:96,stroke:css(l[1]),'stroke-width':4,'stroke-linecap':'round'},svg);
el('text',{x:x+l[0],y:108,'text-anchor':'middle','font-family':f,'font-size':9,'font-weight':700,fill:ink},svg,H(l[2])); }); }
pot(45,'ENERGY','P22'); pot(115,'DRIFT','P23');
el('circle',{cx:190,cy:42,r:16,fill:'#2C3238'},svg); el('text',{x:190,y:14,'text-anchor':'middle','font-family':f,'font-size':10,fill:ink},svg,'SEED');
[[-7,'--w-yellow','P26'],[7,'--w-black','P28']].forEach(function(l){ el('line',{x1:190+l[0],y1:58,x2:190+l[0],y2:96,stroke:css(l[1]),'stroke-width':4,'stroke-linecap':'round'},svg); el('text',{x:190+l[0],y:108,'text-anchor':'middle','font-family':f,'font-size':9,'font-weight':700,fill:ink},svg,H(l[2])); });
el('rect',{x:240,y:30,width:34,height:24,rx:3,fill:'#2C3238'},svg); el('text',{x:257,y:14,'text-anchor':'middle','font-family':f,'font-size':10,fill:ink},svg,'RUN');
[[-11,null,''],[0,'--w-yellow','P27'],[11,'--w-black','P28']].forEach(function(l){ el('rect',{x:257+l[0]-2.5,y:54,width:5,height:10,fill:'#C9CFD5'},svg); if(l[1]){ el('line',{x1:257+l[0],y1:64,x2:257+l[0],y2:96,stroke:css(l[1]),'stroke-width':4,'stroke-linecap':'round'},svg); el('text',{x:257+l[0],y:108,'text-anchor':'middle','font-family':f,'font-size':9,'font-weight':700,fill:ink},svg,H(l[2])); } });
el('text',{x:150,y:138,'text-anchor':'middle','font-family':f,'font-size':9,fill:m},svg,'lugs facing down, seen from the front');
}
/* ---------- Wire map: every panel wire, by part, with its board hole ---------- */
var WMAP=[ // [pad, part, lug, short label]
['P29','Buttons','Row 1 · KICK, KICK 2, SNARE','ROW 1'],['P30','Buttons','Row 2 · HAT, HAT 16, PERC, GHOST','ROW 2'],['P31','Buttons','Row 3 · BASS, MEL A, MEL B, PAD','ROW 3'],
['P32','Buttons','Col 1 · KICK, HAT, BASS (diode band side)','COL 1'],['P33','Buttons','Col 2 · KICK 2, HAT 16, MEL A','COL 2'],['P34','Buttons','Col 3 · SNARE, PERC, MEL B','COL 3'],['P35','Buttons','Col 4 · GHOST, PAD','COL 4'],
['P26','SEED','one lug','SEED'],['P27','RUN toggle','middle lug (it sits in the button grid, row 1 / col 4)','RUN'],['P28','SEED + RUN','ground: SEED other lug, RUN one outer lug','GND BUS'],
['P20','Encoder','VCC (3.3 V, never 5 V)','ENC VCC'],['P21','Encoder','GND','ENC GND'],['P19','Encoder','SW','ENC SW'],['P18','Encoder','SIB','ENC SIB'],['P17','Encoder','SIA','ENC SIA'],
['P22','ENERGY pot','middle lug','ENERGY'],['P23','DRIFT pot','middle lug','DRIFT'],['P24','Pots','right lugs, both (3.3 V)','POTS +'],['P25','Pots','left lugs, both (AGND)','POTS −'],
['P14','LCD','1 · VCC (3.3 V)','LCD VCC'],['P15','LCD','2 · GND','LCD GND'],['P10','LCD','3 · SCE','LCD SCE'],['P9','LCD','4 · RST','LCD RST'],['P11','LCD','5 · D/C','LCD D/C'],['P12','LCD','6 · DN (MOSI)','LCD DN'],['P13','LCD','7 · SCLK','LCD SCLK'],['P16','LCD','8 · LED, through a 330 Ω resistor (if dark, move to any AA hole)','LCD LED'],
['P2','MIDI IN jack','tip','IN tip'],['P1','MIDI IN jack','ring','IN ring'],
['P3','OUT KIT jack','tip','KIT tip'],['P4','OUT KIT jack','ring','KIT ring'],['P5','OUT KIT jack','sleeve','KIT slv'],
['P6','OUT SYNTH jack','tip','SYN tip'],['P7','OUT SYNTH jack','ring','SYN ring'],['P8','OUT SYNTH jack','sleeve','SYN slv']
];
function wireOf(p){ return L.wires[+p.slice(1)-1]; }
function drawMap(){
var svg=document.getElementById('wmap'); if(!svg) return; svg.innerHTML='';
var LM=10.5, RM=10.5, f=css('--f-mono'), ink=css('--ink');
svg.setAttribute('viewBox',(-LM)+' -3.2 '+(L.W+LM+RM)+' '+(L.H+4.6));
el('rect',{x:-0.8,y:-0.5,width:L.W+0.6,height:L.H+0.3,rx:0.4,fill:css('--board'),stroke:css('--board-edge'),'stroke-width':0.12,opacity:0.55},svg);
for(var r=0;r<L.H;r++) el('line',{x1:-0.4,y1:r,x2:L.W-0.6,y2:r,stroke:css('--copper'),'stroke-width':0.5,opacity:0.18},svg);
el('rect',{x:11.3,y:-0.55,width:8.4,height:20.1,rx:0.4,fill:css('--part'),opacity:0.85},svg);
el('text',{x:15.5,y:9.5,'text-anchor':'middle','font-family':f,'font-size':'1.1','font-weight':700,fill:css('--part-ink')},svg,'PICO');
el('text',{x:15.5,y:-1.2,'text-anchor':'middle','font-family':f,'font-size':'0.7',fill:css('--muted')},svg,'USB edge');
el('rect',{x:3.6,y:19.6,width:3.8,height:3.8,rx:0.2,fill:css('--part'),opacity:0.85},svg);
el('text',{x:5.5,y:21.8,'text-anchor':'middle','font-family':f,'font-size':'0.6',fill:css('--part-ink')},svg,'opto');
// strip letters on both sides, hole numbers along the bottom
for(r=0;r<L.H;r++){ el('text',{x:-1.0,y:r+0.22,'text-anchor':'end','font-family':f,'font-size':'0.5',fill:css('--muted')},svg,rowName(r)); el('text',{x:L.W-0.1,y:r+0.22,'font-family':f,'font-size':'0.5',fill:css('--muted')},svg,rowName(r)); }
for(var c=0;c<L.W;c+=2) el('text',{x:c,y:L.H+0.9,'text-anchor':'middle','font-family':f,'font-size':'0.5',fill:css('--muted')},svg,String(c+1));
var sides={L:[],R:[]};
WMAP.forEach(function(m){ var w=wireOf(m[0]); (w.h[0]<=12?sides.L:sides.R).push({m:m,w:w}); });
['L','R'].forEach(function(sd){
var list=sides[sd].sort(function(a,b){ return a.w.h[1]-b.w.h[1] || (sd==='L'?b.w.h[0]-a.w.h[0]:a.w.h[0]-b.w.h[0]); }), last=-9, k;
for(k=0;k<list.length;k++){ list[k].ly=Math.max(list[k].w.h[1],last+1.05); last=list[k].ly; }
var cap=L.H-0.6; // keep labels inside the board's height: push the stack up from the bottom
for(k=list.length-1;k>=0;k--){ list[k].ly=Math.min(list[k].ly,cap); cap=list[k].ly-1.05; }
list.forEach(function(o){
var h=o.w.h, col=css(COL[o.w.col]), ly=o.ly;
var lx=sd==='L'?-1.9:L.W+0.9;
el('polyline',{points:h[0]+','+h[1]+' '+(sd==='L'?-0.6:L.W-0.4)+','+ly+' '+lx+','+ly,fill:'none',stroke:col,'stroke-width':0.13,opacity:0.9},svg);
el('circle',{cx:h[0],cy:h[1],r:0.36,fill:col,stroke:'#fff','stroke-width':0.08},svg);
var t=el('text',{x:sd==='L'?lx-0.25:lx+0.25,y:ly+0.25,'text-anchor':sd==='L'?'end':'start','font-family':f,'font-size':'0.72',fill:ink},svg);
var a=document.createElementNS(NS,'tspan'); a.setAttribute('font-weight','700'); a.textContent=o.m[3]+' '; t.appendChild(a);
var b=document.createElementNS(NS,'tspan'); b.setAttribute('fill',css('--accent')); b.setAttribute('font-weight','700'); b.textContent=pos(h); t.appendChild(b);
});
});
}
function checklist(){
var tb=document.getElementById('wcheck'); if(!tb) return; tb.innerHTML='';
var done={}; try{ done=JSON.parse(localStorage.getItem('seedbox.wires')||'{}')||{}; }catch(e){}
var prev='';
WMAP.forEach(function(m){
var w=wireOf(m[0]), tr=document.createElement('tr'), part=m[1]===prev?'':m[1]; prev=m[1];
if(part) tr.className='grp';
var td0=document.createElement('td'), cb=document.createElement('input'); cb.type='checkbox'; cb.checked=!!done[m[0]]; cb.setAttribute('aria-label','Done: '+m[1]+' '+m[2]);
cb.addEventListener('change',function(){ done[m[0]]=cb.checked; tr.classList.toggle('done',cb.checked); try{ localStorage.setItem('seedbox.wires',JSON.stringify(done)); }catch(e){} });
td0.appendChild(cb); tr.appendChild(td0); tr.classList.toggle('done',cb.checked);
var td1=document.createElement('td'); td1.className='part'; td1.textContent=part; tr.appendChild(td1);
var td2=document.createElement('td'); td2.textContent=m[2]; tr.appendChild(td2);
var td3=document.createElement('td'), sw=document.createElement('span'); sw.className='sw'; sw.style.background='var('+COL[w.col]+')'; td3.appendChild(sw); td3.appendChild(document.createTextNode(w.col)); tr.appendChild(td3);
var td4=document.createElement('td'); td4.className='hole'; td4.textContent=pos(w.h); tr.appendChild(td4);
tb.appendChild(tr);
});
}
function all(){
drawMap(); checklist(); drawTop(); drawBottom(); tables(); drawMatrix(); drawDin();
pinRow('lcd',[['1 VCC','--w-red','P14'],['2 GND','--w-black','P15'],['3 SCE','--w-blue','P10'],['4 RST','--w-blue','P9'],['5 D/C','--w-blue','P11'],['6 DN','--w-blue','P12'],['7 SCLK','--w-blue','P13'],['8 LED','--w-red','P16']]);
pinRow('enc',[['VCC','--w-red','P20'],['GND','--w-black','P21'],['SW','--w-green','P19'],['SIB','--w-green','P18'],['SIA','--w-green','P17']]);
drawMisc();
}
all();
try{ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change',all); }catch(e){}
})();
</script>