Seedbox v0.1 + firmware, generic kit/synth, ROOT/SCALE pages
Some checks failed
check / check (push) Has been cancelled
Some checks failed
check / check (push) Has been cancelled
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>
This commit is contained in:
243
tools/bom.html
Normal file
243
tools/bom.html
Normal file
@@ -0,0 +1,243 @@
|
||||
<title>Seedbox Build Sheet</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 workbench sheet. Parts list grouped by subsystem with a tick-off column, then pin map and wiring notes. */
|
||||
:root{
|
||||
--bg:#E8ECEF; --surface:#F6F7F9; --ink:#18202A; --muted:#566271; --line:#C3CBD3; --accent:#1E4C9E;
|
||||
--have:#1D7A4F; --check:#9A6B10; --buy:#B4441F;
|
||||
--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;
|
||||
--have:#5CCB92; --check:#E2B455; --buy:#F07B52; color-scheme:dark } }
|
||||
:root[data-theme="dark"]{
|
||||
--bg:#12161B; --surface:#1A1F25; --ink:#E2E7EC; --muted:#8C98A5; --line:#333B44; --accent:#8EB0F2;
|
||||
--have:#5CCB92; --check:#E2B455; --buy:#F07B52; color-scheme:dark }
|
||||
*{box-sizing:border-box}
|
||||
body{background:var(--bg);color:var(--ink);font:15px/1.55 var(--f-ui);padding-block:28px 48px;padding-inline:16px}
|
||||
.wrap{max-width:980px;margin:0 auto;display:grid;gap:22px}
|
||||
h1{font:700 24px/1.15 var(--f-mono);margin:0;letter-spacing:-.01em}
|
||||
h2{font:700 13px var(--f-mono);letter-spacing:.08em;text-transform:uppercase;color:var(--accent);margin:0}
|
||||
.sub{color:var(--muted);margin:6px 0 0;max-width:68ch}
|
||||
.bar{display:flex;flex-wrap:wrap;gap:10px 16px;align-items:center;justify-content:space-between;background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:12px 14px}
|
||||
.tally{display:flex;flex-wrap:wrap;gap:6px 18px;font:500 12px var(--f-mono);font-variant-numeric:tabular-nums;color:var(--muted)}
|
||||
.tally b{color:var(--ink)}
|
||||
.ctrls{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
|
||||
.btn{font:600 12px var(--f-mono);border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:6px;padding:7px 12px;cursor:pointer}
|
||||
.btn:hover{border-color:var(--accent)}
|
||||
.btn:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
||||
.check{display:flex;gap:8px;align-items:center;font:500 13px var(--f-ui);color:var(--muted)}
|
||||
section{display:grid;gap:10px}
|
||||
.tbl{overflow-x:auto;background:var(--surface);border:1px solid var(--line);border-radius:10px}
|
||||
table{width:100%;border-collapse:collapse;min-width:640px}
|
||||
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:9px 12px;border-bottom:1px solid var(--line);vertical-align:top}
|
||||
tr:last-child td{border-bottom:0}
|
||||
td.part{font-weight:600}
|
||||
td.part small{display:block;font-weight:400;color:var(--muted);font-size:12px}
|
||||
td.qty{font:500 13px var(--f-mono);font-variant-numeric:tabular-nums;white-space:nowrap}
|
||||
td.note{color:var(--muted);font-size:13px;max-width:44ch}
|
||||
td.tick{width:44px;text-align:center}
|
||||
td.tick input{width:18px;height:18px;accent-color:var(--have)}
|
||||
.st{display:inline-block;font:600 10px var(--f-mono);letter-spacing:.06em;text-transform:uppercase;padding:2px 7px;border-radius:999px;border:1px solid currentColor;white-space:nowrap}
|
||||
.st.have{color:var(--have)} .st.check{color:var(--check)} .st.buy{color:var(--buy)} .st.opt{color:var(--muted)}
|
||||
tr.got td:not(.tick){opacity:.5}
|
||||
tr.got td.part{text-decoration:line-through}
|
||||
.hide-got tr.got{display:none}
|
||||
.mono{font-family:var(--f-mono);font-size:12.5px}
|
||||
.pins td:first-child{font:600 13px var(--f-mono);white-space:nowrap}
|
||||
.cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px}
|
||||
.card{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:14px 16px;min-width:0}
|
||||
.card h3{font:700 14px var(--f-ui);margin:0 0 8px}
|
||||
.card ul{margin:0;padding-left:18px;display:grid;gap:4px;font-size:14px}
|
||||
.card p{margin:0 0 8px;font-size:14px}
|
||||
pre{font:400 12px/1.5 var(--f-mono);margin:0;overflow-x:auto;color:var(--ink)}
|
||||
.gone{color:var(--muted);font-size:14px}
|
||||
</style>
|
||||
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<h1>Seedbox build sheet</h1>
|
||||
<p class="sub">Everything for the Pico 2W build: Nokia 5110 screen with the on-screen track map, 11 track buttons in a diode matrix, encoder, two pots, SEED, RUN, one MIDI in and two MIDI outs. "Have" means it was on your parts list; tick items off as you find them in the drawers.</p>
|
||||
</header>
|
||||
|
||||
<div class="bar">
|
||||
<div class="tally" id="tally"></div>
|
||||
<div class="ctrls">
|
||||
<label class="check"><input type="checkbox" id="hideGot"> Hide ticked</label>
|
||||
<button class="btn" id="copyBuy" type="button">Copy shopping list</button>
|
||||
<button class="btn" id="reset" type="button">Clear ticks</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="parts"></div>
|
||||
|
||||
<section>
|
||||
<h2>Pin map</h2>
|
||||
<div class="tbl"><table class="pins">
|
||||
<thead><tr><th>GPIO</th><th>Function</th><th>Connects to</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>GP0</td><td>UART0 TX · MIDI OUT A</td><td>10 Ω → OUT KIT tip (pin 5)</td></tr>
|
||||
<tr><td>GP1</td><td>UART0 RX · MIDI IN</td><td>6N137 pin 6 (1 kΩ pull-up to 3V3)</td></tr>
|
||||
<tr><td>GP4</td><td>UART1 TX · MIDI OUT B</td><td>10 Ω → OUT SYNTH tip (pin 5)</td></tr>
|
||||
<tr><td>GP2 GP3 GP5</td><td>Button matrix rows</td><td>Inputs with internal pull-ups</td></tr>
|
||||
<tr><td>GP6 GP7 GP8 GP9</td><td>Button matrix columns</td><td>Driven low one at a time</td></tr>
|
||||
<tr><td>GP10 GP11</td><td>Encoder A / B</td><td>Encoder module SIA / SIB</td></tr>
|
||||
<tr><td>GP12</td><td>Encoder push</td><td>Encoder module SW</td></tr>
|
||||
<tr><td>GP13</td><td>SEED button</td><td>Button to GND, internal pull-up</td></tr>
|
||||
<tr><td>GP14</td><td>RUN toggle</td><td>Toggle to GND, internal pull-up</td></tr>
|
||||
<tr><td>GP17</td><td>LCD SCE</td><td>Can be tied to GND instead (frees the pin)</td></tr>
|
||||
<tr><td>GP18</td><td>SPI0 SCK</td><td>LCD SCLK</td></tr>
|
||||
<tr><td>GP19</td><td>SPI0 TX</td><td>LCD DN (MOSI)</td></tr>
|
||||
<tr><td>GP20</td><td>LCD DC</td><td>LCD D/C</td></tr>
|
||||
<tr><td>GP21</td><td>LCD RST</td><td>LCD RST</td></tr>
|
||||
<tr><td>GP22</td><td>Free</td><td>Only for backlight dimming later, through a transistor</td></tr>
|
||||
<tr><td>GP26</td><td>ADC0 · Energy pot</td><td>Pot wiper (ends to 3V3 and AGND)</td></tr>
|
||||
<tr><td>GP27</td><td>ADC1 · Drift pot</td><td>Pot wiper (ends to 3V3 and AGND)</td></tr>
|
||||
<tr><td>GP15 GP16 GP28</td><td>Spare</td><td>GP28 is also an analog input</td></tr>
|
||||
<tr><td>GP23 24 25 29</td><td>Reserved</td><td>Used by the Pico 2W's wireless chip, don't wire</td></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Wiring notes</h2>
|
||||
<div class="cols">
|
||||
<div class="card">
|
||||
<h3>MIDI OUT (×2, 3.3 V spec, TRS Type A)</h3>
|
||||
<pre>3V3 ──33 Ω── ring (DIN pin 4)
|
||||
TX ──10 Ω── tip (DIN pin 5)
|
||||
GND ──────── sleeve (DIN pin 2)</pre>
|
||||
<p style="margin-top:8px">Lug order differs between jack models: find tip, ring and sleeve with the meter and a plug inserted.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>MIDI IN (6N137)</h3>
|
||||
<pre>ring ──220 Ω── opto 2 (anode)
|
||||
tip ───────── opto 3 (cathode)
|
||||
1N4148 across 2–3, band to pin 2
|
||||
opto 8 → VBUS (5 V) opto 5 → GND
|
||||
opto 6 → GP1, 1 kΩ to 3V3
|
||||
opto 7 ──────── opto 8 (enable high)
|
||||
100 nF across 8–5 (required)</pre>
|
||||
<p style="margin-top:8px">Pull the output up to 3V3, never 5 V. The sleeve stays unconnected on the input.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Button matrix (3 × 4)</h3>
|
||||
<pre> C0 GP6 C1 GP7 C2 GP8 C3 GP9
|
||||
R0 GP2 KICK KICK 2 SNARE (RUN toggle, not in matrix)
|
||||
R1 GP3 HAT HAT 16 PERC GHOST
|
||||
R2 GP5 BASS MEL A MEL B PAD</pre>
|
||||
<p style="margin-top:8px">Same layout as the lid: the RUN toggle sits in row 1 / column 4, but it has its own pin (GP14), so that matrix slot is empty. Each button: row → switch → 1N4148 → column, band toward the column. The diodes let you hold several buttons at once without phantom presses.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Nokia 5110 module</h3>
|
||||
<ul>
|
||||
<li>VCC to 3V3, not 5 V. Logic connects straight to the Pico, no resistors.</li>
|
||||
<li>Backlight polarity differs between red and blue boards. Test which way yours lights (BL to GND or to 3V3), then either hard-wire it or switch that side with a transistor for dimming (NPN on the ground side, PNP on the 3.3 V side).</li>
|
||||
<li>Contrast is set in firmware and drifts a little with temperature. Expect to tune it once.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Encoder and pots</h3>
|
||||
<ul>
|
||||
<li>Power the encoder module from 3V3. Its onboard pull-ups would otherwise put 5 V on the Pico pins.</li>
|
||||
<li>Pots: one end to 3V3, the other to AGND (pin 33), wiper to the ADC pin. A 100 nF cap from each wiper to AGND calms jitter.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Panel layout</h3>
|
||||
<ul>
|
||||
<li>See the panel layout page (landscape): screen with the encoder on the left, ENERGY / DRIFT / SEED in the middle, the 4 × 3 block on the right (11 track buttons, RUN toggle top-right).</li>
|
||||
<li>Screen sits on the lid, wires through one hole hidden under the module. USB and the three jacks on the back wall; the Pico's USB end sits at the stripboard edge, behind the slot.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>No longer needed</h2>
|
||||
<p class="gone">USB panel extension, standoffs and screws (stripboard glued behind a slot instead) · 0.91" OLED (replaced by the Nokia screen) · WS2812 LED strip (the screen shows track state) · 4.7 kΩ opto resistor (6N138 only) · 470 Ω resistors (old 5 V MIDI out values) · 5-pin DIN jacks (3.5 mm TRS instead).</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
/* status: have = on your parts list, check = probably in the drawer, buy = likely missing, opt = optional */
|
||||
var GROUPS=[
|
||||
{name:'Core',rows:[
|
||||
['pico','Raspberry Pi Pico 2W','RP2350, micro-USB','1','have','The whole engine runs on it with lots of headroom.'],
|
||||
['lcd','Nokia 5110 LCD module','PCD8544, 84×48, SPI','1','have','The module you used with Arduino.'],
|
||||
['box','Enclosure','','1','have','About 80 × 150 × 40 mm. Panel layout: tools/panel.html.']
|
||||
]},
|
||||
{name:'Controls',rows:[
|
||||
['enc','Rotary encoder with push','KY-040 module or bare EC11','1','have','Power it from 3V3.'],
|
||||
['pots','Potentiometer, linear','B10K (anything 5k–50k linear works)','2','have','Energy and Drift.'],
|
||||
['knobs','Knobs','to fit the pot and encoder shafts','3','check','Shaft types differ (D, knurled, 6 mm). Match them.'],
|
||||
['btn','Momentary push button, panel mount','~12 mm, non-latching','11','check','7 kit + 4 line tracks.'],
|
||||
['seedbtn','Momentary push button, large','16–22 mm, non-latching','1','check','SEED. Any big momentary works.'],
|
||||
['tog','Toggle switch, SPDT','','1','have','RUN / STOP. Your old bypass switch.'],
|
||||
['dio','1N4148 diode','or 1N4448 / 1N914 / any small-signal diode','12','buy','11 for the button matrix + 1 for MIDI IN. 1N4448 works too. Every button needs one: a missing diode gives phantom presses with three buttons held.']
|
||||
]},
|
||||
{name:'MIDI',rows:[
|
||||
['trs','3.5 mm stereo (TRS) jack, panel mount','3 lugs: tip, ring, sleeve. Not mono.','3','check','IN, OUT KIT, OUT SYNTH. MIDI TRS Type A. 6 mm hole each.'],
|
||||
['adapt','TRS-to-DIN MIDI adapter, Type A','female DIN, 3.5 mm plug','1','buy','For a DIN synth or sampler (e.g. MiniFreak). Must say Type A (Type B swaps tip and ring). Type A TRS gear (e.g. SP-404MKII) takes a plain stereo 3.5 mm cable.'],
|
||||
['opto','6N137 optocoupler','DIP-8 (6N138 also works: then drop the pin 7 link)','1','have','MIDI IN. You have three; one is used.'],
|
||||
['sock','DIP-8 IC socket','','1','buy','So the opto can be swapped.'],
|
||||
['r330','Resistor 330 Ω','220–470 Ω','1','check','LCD backlight (LED pin) to 3.3 V, inline at the module.'],
|
||||
['r220','Resistor 220 Ω','150–220 Ω','1','have','MIDI IN, in series with the opto LED. The 6N137 wants a bit more LED current: use 150 Ω if learning misses notes.'],
|
||||
['r1k','Resistor 1 kΩ','470 Ω–2.2 kΩ is fine','2','have','Opto output pull-up, NPN base.'],
|
||||
['r33','Resistor 33 Ω, ½ W','27–47 Ω is fine','2','buy','MIDI OUT ring (pin 4) line, per the MIDI 3.3 V spec. ½ W because a mono plug in an OUT jack shorts it straight to ground (0.33 W); a ¼ W one would cook.'],
|
||||
['r10','Resistor 10 Ω','10–22 Ω is fine','2','check','MIDI OUT tip (pin 5) line, from TX. Don\'t replace with a wire: it protects the pin.'],
|
||||
['cap','Ceramic capacitor 100 nF','anything 47–220 nF','3','check','C1 on the board (6N137 supply bypass, required). Optional: one per pot, soldered at the pot from wiper to the left lug, if the readings jitter.']
|
||||
]},
|
||||
{name:'Screen extras',rows:[
|
||||
['blt','NPN or PNP transistor','2N3904 / BC547 (NPN) or 2N3906 / BC557 (PNP)','1','opt','Only for a dimmable backlight. NPN if your BL lights when grounded, PNP if it lights from 3.3 V. Otherwise hard-wire it.']
|
||||
]},
|
||||
{name:'Power and assembly',rows:[
|
||||
['perf','Stripboard','at least 34 × 28 holes (about 86 × 71 mm)','1','check','Pico socketed at the top edge, so its USB port meets a slot in the back wall.'],
|
||||
['hdr','Female header strip, 1 × 20','2.54 mm','2','check','Socket the Pico instead of soldering it in.'],
|
||||
['mhdr','Male header strip, 1 × 20','2.54 mm','2','check','Soldered to the Pico so it plugs into the female headers. Skip if yours is a Pico 2 WH (headers fitted).'],
|
||||
['tape','Double-sided foam tape or hot glue','','—','check','Holds the stripboard and the screen without screws.']
|
||||
]}
|
||||
];
|
||||
var LABEL={have:'have',check:'check',buy:'buy',opt:'optional'};
|
||||
var ticks={}; try{ ticks=JSON.parse(localStorage.getItem('seedbox.bom')||'{}')||{}; }catch(e){ ticks={}; }
|
||||
function saveTicks(){ try{ localStorage.setItem('seedbox.bom',JSON.stringify(ticks)); }catch(e){} }
|
||||
var root=document.getElementById('parts'), rowEls={};
|
||||
GROUPS.forEach(function(g){
|
||||
var sec=document.createElement('section'), h=document.createElement('h2'); h.textContent=g.name; sec.appendChild(h);
|
||||
var wrap=document.createElement('div'); wrap.className='tbl';
|
||||
var t=document.createElement('table'); t.innerHTML='<thead><tr><th>Got</th><th>Part</th><th>Qty</th><th>Status</th><th>Notes</th></tr></thead>';
|
||||
var tb=document.createElement('tbody');
|
||||
g.rows.forEach(function(r){
|
||||
var tr=document.createElement('tr'), id='p_'+r[0];
|
||||
tr.innerHTML='<td class="tick"><input type="checkbox" id="'+id+'" aria-label="Got '+r[1].replace(/"/g,'')+'"></td>'+
|
||||
'<td class="part"></td><td class="qty"></td><td><span class="st '+r[4]+'">'+LABEL[r[4]]+'</span></td><td class="note"></td>';
|
||||
var p=tr.querySelector('.part'); p.textContent=r[1]; if(r[2]){ var sm=document.createElement('small'); sm.textContent=r[2]; p.appendChild(sm); }
|
||||
tr.querySelector('.qty').textContent=r[3]; tr.querySelector('.note').textContent=r[5];
|
||||
var cb=tr.querySelector('input'); cb.checked=!!ticks[r[0]]; tr.classList.toggle('got',cb.checked);
|
||||
cb.addEventListener('change',function(){ ticks[r[0]]=cb.checked; saveTicks(); tr.classList.toggle('got',cb.checked); tally(); });
|
||||
rowEls[r[0]]={tr:tr,row:r}; tb.appendChild(tr);
|
||||
});
|
||||
t.appendChild(tb); wrap.appendChild(t); sec.appendChild(wrap); root.appendChild(sec);
|
||||
});
|
||||
function tally(){
|
||||
var n=0,got=0,buy=0,miss=[];
|
||||
Object.keys(rowEls).forEach(function(k){ var r=rowEls[k].row; n++; if(ticks[k]) got++; else if(r[4]!=='opt'){ miss.push(r); if(r[4]==='buy') buy++; } });
|
||||
document.getElementById('tally').innerHTML='<span><b>'+got+'</b> of '+n+' ticked</span><span><b>'+miss.length+'</b> still to find</span><span><b>'+buy+'</b> likely to buy</span>';
|
||||
return miss;
|
||||
}
|
||||
tally();
|
||||
var hide=document.getElementById('hideGot');
|
||||
hide.addEventListener('change',function(){ root.classList.toggle('hide-got',hide.checked); });
|
||||
document.getElementById('reset').addEventListener('click',function(){ ticks={}; saveTicks(); Object.keys(rowEls).forEach(function(k){ rowEls[k].tr.querySelector('input').checked=false; rowEls[k].tr.classList.remove('got'); }); tally(); });
|
||||
document.getElementById('copyBuy').addEventListener('click',function(){
|
||||
var b=this, txt=tally().map(function(r){ return r[3]+' × '+r[1]+(r[2]?' ('+r[2]+')':''); }).join('\n');
|
||||
function ok(){ b.textContent='Copied'; setTimeout(function(){ b.textContent='Copy shopping list'; },1400); }
|
||||
try{ navigator.clipboard.writeText(txt).then(ok,function(){ b.textContent='Copy failed'; }); }catch(e){ b.textContent='Copy failed'; }
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
6
tools/build-wiring.js
Normal file
6
tools/build-wiring.js
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Rebuilds tools/wiring.html from tools/wiring-src/* and hardware/layout.js (the one source of truth).
|
||||
Run after editing the layout: node hardware/check-layout.js && node tools/build-wiring.js */
|
||||
var fs=require('fs'), path=require('path'), d=__dirname;
|
||||
function rd(p){ return fs.readFileSync(path.join(d,p),'utf8'); }
|
||||
var html=rd('wiring-src/head.html')+rd('wiring-src/body.html')+'<script>\n'+rd('../hardware/layout.js')+'\n</script><script>'+rd('wiring-src/render.js')+'</script>';
|
||||
fs.writeFileSync(path.join(d,'wiring.html'),html); console.log('wrote tools/wiring.html',html.length,'bytes');
|
||||
270
tools/panel.html
Normal file
270
tools/panel.html
Normal file
@@ -0,0 +1,270 @@
|
||||
<title>Seedbox Panel Layout</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: to-scale drawing of the box unfolded (end wall, lid, side wall) beside its inputs; drill table below. */
|
||||
:root{
|
||||
--bg:#E8ECEF; --surface:#F6F7F9; --ink:#18202A; --muted:#566271; --line:#C3CBD3; --accent:#1E4C9E; --warn:#B4441F;
|
||||
--metal:#23272C; --metal-edge:#3A4047; --print:#D9DEE3; --pcb:#B8282A; --lcd:#A6B59A;
|
||||
--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;
|
||||
--metal:#0B0D10; --metal-edge:#4A525B; color-scheme:dark } }
|
||||
:root[data-theme="dark"]{
|
||||
--bg:#12161B; --surface:#1A1F25; --ink:#E2E7EC; --muted:#8C98A5; --line:#333B44; --accent:#8EB0F2; --warn:#F07B52;
|
||||
--metal:#0B0D10; --metal-edge:#4A525B; color-scheme:dark }
|
||||
*{box-sizing:border-box}
|
||||
body{background:var(--bg);color:var(--ink);font:15px/1.55 var(--f-ui);padding-block:28px 48px;padding-inline:16px}
|
||||
.wrap{max-width:1100px;margin:0 auto;display:grid;gap:22px}
|
||||
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}
|
||||
.sub{color:var(--muted);margin:6px 0 0;max-width:72ch}
|
||||
.main{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:22px;align-items:start}
|
||||
@media (max-width:860px){.main{grid-template-columns:minmax(0,1fr)}}
|
||||
.draw{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:14px;min-width:0}
|
||||
.draw svg{display:block;width:100%;height:auto;max-height:82vh}
|
||||
.side{display:grid;gap:14px}
|
||||
.card{background:var(--surface);border:1px solid var(--line);border-radius:10px;padding:14px 16px;display:grid;gap:10px}
|
||||
.fields{display:grid;grid-template-columns:1fr 1fr;gap:10px 12px}
|
||||
.field{display:grid;gap:4px;min-width:0}
|
||||
.field label{font:500 11px var(--f-mono);color:var(--muted)}
|
||||
input[type=number]{font:500 14px var(--f-mono);color:var(--ink);background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:6px 8px;width:100%}
|
||||
input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
||||
.seg{display:grid;grid-template-columns:1fr 1fr;border:1px solid var(--line);border-radius:7px;overflow:hidden}
|
||||
.segb{font:600 12px var(--f-mono);padding:8px;border:0;background:var(--bg);color:var(--muted);cursor:pointer}
|
||||
.segb[aria-pressed="true"]{background:var(--accent);color:var(--surface)}
|
||||
.segb:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
||||
.warns{display:grid;gap:6px;font-size:13px}
|
||||
.warns .ok{color:var(--muted)}
|
||||
.warns .bad{color:var(--warn);font-weight:600}
|
||||
.note{color:var(--muted);font-size:13px;margin:0}
|
||||
.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}
|
||||
td.n{font:500 13px var(--f-mono);font-variant-numeric:tabular-nums;white-space:nowrap}
|
||||
tr:last-child td{border-bottom:0}
|
||||
.cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px}
|
||||
.cols ul{margin:0;padding-left:18px;display:grid;gap:4px;font-size:14px}
|
||||
</style>
|
||||
|
||||
<div class="wrap">
|
||||
<header>
|
||||
<h1>Seedbox panel layout</h1>
|
||||
<p class="sub">Lid and walls, drawn to scale and unfolded, in portrait or landscape. The box size is an estimate from your photo, using the red Nokia module (about 44 mm square) as the ruler. Measure the real box and type it in: everything re-flows and the drill table updates.</p>
|
||||
</header>
|
||||
|
||||
<div class="main">
|
||||
<div class="draw"><svg id="svg" role="img" aria-label="To-scale panel layout"></svg></div>
|
||||
<div class="side">
|
||||
<div class="card">
|
||||
<h2>Box</h2>
|
||||
<div class="seg" role="group" aria-label="Orientation">
|
||||
<button type="button" class="segb" id="oP" aria-pressed="false">Portrait</button>
|
||||
<button type="button" class="segb" id="oL" aria-pressed="true">Landscape</button>
|
||||
</div>
|
||||
<div class="fields">
|
||||
<div class="field"><label for="W">Short side, mm</label><input type="number" id="W" value="80" min="50" max="200"></div>
|
||||
<div class="field"><label for="L">Long side, mm</label><input type="number" id="L" value="150" min="80" max="300"></div>
|
||||
<div class="field"><label for="H">Box height, mm</label><input type="number" id="H" value="40" min="20" max="120"></div>
|
||||
<div class="field"><label for="E">Lip / wall margin, mm</label><input type="number" id="E" value="3" min="0" max="15"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Parts</h2>
|
||||
<div class="fields">
|
||||
<div class="field"><label for="BH">Track button hole Ø</label><input type="number" id="BH" value="12" min="6" max="24"></div>
|
||||
<div class="field"><label for="BN">Its nut / cap Ø</label><input type="number" id="BN" value="15" min="6" max="30"></div>
|
||||
<div class="field"><label for="SH">SEED hole Ø</label><input type="number" id="SH" value="16" min="6" max="30"></div>
|
||||
<div class="field"><label for="SN">SEED nut / cap Ø</label><input type="number" id="SN" value="20" min="8" max="36"></div>
|
||||
<div class="field"><label for="K">Knob Ø (pots, encoder)</label><input type="number" id="K" value="17" min="8" max="35"></div>
|
||||
<div class="field"><label for="D">MIDI jack hole Ø (3.5 mm TRS)</label><input type="number" id="D" value="6" min="4" max="22" step="0.5"></div>
|
||||
</div>
|
||||
<p class="note">Measure your actual buttons and knobs. The nut or cap diameter is what sets how close things can sit.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Fit check</h2>
|
||||
<div class="warns" id="warns"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section style="display:grid;gap:10px">
|
||||
<h2>Drill list</h2>
|
||||
<p class="note">Lid coordinates from the top-left corner as drawn (the cable wall is at the top). Wall coordinates: x from the left as drawn, height down from the lid edge or up from the inside floor.</p>
|
||||
<div class="tbl"><table><thead><tr><th>Where</th><th>Part</th><th>x mm</th><th>y mm</th><th>Hole</th></tr></thead><tbody id="drill"></tbody></table></div>
|
||||
</section>
|
||||
|
||||
<section class="cols">
|
||||
<div class="card">
|
||||
<h2>Why this arrangement</h2>
|
||||
<ul id="why"></ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Before drilling</h2>
|
||||
<ul>
|
||||
<li>Check the inside, not just the lid: pot bodies (~17 mm) and button bodies hang down from the lid, and the socketed Pico stands about 13 mm off the stripboard. Under SEED sit R4 (flat) and pads P3/P4: keep those wires low, and check the SEED button's depth.</li>
|
||||
<li>The stripboard lies on the floor with strip A against the back wall. The Pico's USB socket is about 41 mm from the board's left edge and about 16 mm above the inside floor (foam tape, board, female header, the Pico's header spacer): measure your stack and adjust the slot before cutting it. The slot has to clear the plug's moulding, not just the metal tip.</li><li>MIDI jacks: 3.5 mm stereo panel jacks, a 6 mm hole and a nut each.</li>
|
||||
<li>The screen's wire hole goes under the module's 8-pin header, so the module hides it.</li>
|
||||
<li>Centre-punch every mark and step-drill the big holes (a step bit is kinder to plastic too: go slowly so it doesn't crack).</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var NS='http://www.w3.org/2000/svg', svg=document.getElementById('svg');
|
||||
var ids=['W','L','H','E','BH','BN','SH','SN','K','D'];
|
||||
try{ var saved=JSON.parse(localStorage.getItem('seedbox.panel')||'{}'); ids.forEach(function(k){ if(saved[k]!=null) document.getElementById(k).value=saved[k]; }); }catch(e){}
|
||||
function v(id){ var x=parseFloat(document.getElementById(id).value); return isFinite(x)?x:0; }
|
||||
function css(n){ return getComputedStyle(document.documentElement).getPropertyValue(n).trim(); }
|
||||
function el(tag,attrs,parent,txt){ var e=document.createElementNS(NS,tag); for(var k in attrs) e.setAttribute(k,attrs[k]); if(txt!=null) e.textContent=txt; (parent||svg).appendChild(e); return e; }
|
||||
function r1(x){ return Math.round(x*10)/10; }
|
||||
|
||||
var ORIENT='L';
|
||||
var NAMES=[['KICK','KICK 2','SNARE','RUN'],['HAT','HAT 16','PERC','GHOST'],['BASS','MEL A','MEL B','PAD']]; // RUN = the run/stop toggle, in the grid but not in the matrix
|
||||
function buttons(items,cx,cy,px,py,P){ for(var r=0;r<3;r++) for(var c=0;c<4;c++) { var nm=NAMES[r][c], x=cx+(c-1.5)*px, y=cy+(r-1)*py; items.push(nm==='RUN'?{id:'run',name:'RUN toggle',x:x,y:y,hole:6,clr:6,kind:'toggle'}:{id:'b'+r+c,name:nm,x:x,y:y,hole:P.BH,clr:P.BN/2,kind:'btn'}); } }
|
||||
/* Portrait: lid W wide x L deep, jacks on the far short wall, USB on the side wall. */
|
||||
function layoutP(W,L,H,E,P){
|
||||
var m=E+3, items=[], scr={x:m,y:m,w:44,h:44}, colX=(scr.x+scr.w+W-m)/2;
|
||||
items.push({name:'Encoder',x:colX,y:m+11,hole:7,clr:P.K/2,kind:'knob'});
|
||||
var rowY=scr.y+scr.h+P.K/2+5, slot=function(i){ return m+(W-2*m)*(i+0.5)/3; };
|
||||
items.push({name:'ENERGY pot',x:slot(0),y:rowY,hole:7,clr:P.K/2,kind:'knob'});
|
||||
items.push({name:'DRIFT pot',x:slot(1),y:rowY,hole:7,clr:P.K/2,kind:'knob'});
|
||||
items.push({name:'SEED',x:slot(2),y:rowY,hole:P.SH,clr:P.SN/2,kind:'seed'});
|
||||
var bc=P.BN/2, px=Math.min(20,(W-2*m-2*bc)/3), lastY=L-m-bc-4, py=Math.max(px,Math.min(20,(lastY-(rowY+P.K/2+4+bc))/2));
|
||||
buttons(items,W/2,lastY-py,px,py,P);
|
||||
var jacks=[], jn=['MIDI IN','OUT · KIT','OUT · SYNTH'];
|
||||
for(var j=0;j<3;j++) jacks.push({name:jn[j],x:slot(j),z:H/2,hole:P.D,clr:Math.max(5,P.D/2+2)});
|
||||
return {A:W,B:L,m:m,scr:scr,items:items,jacks:jacks,usbBack:null,usbSide:{y:m+22,z:13,w:12,h:7},wire:{x:scr.x+5,y:scr.y+22,hole:8},px:px,py:py,
|
||||
why:['The 12 track buttons are a 4 × 3 block, the same shape as the wiring matrix and the screen map. Synth tracks on the row nearest you.',
|
||||
'Screen and encoder at the far end; knobs and SEED in a middle row. Your hands work the buttons without covering the screen.',
|
||||
'Cables leave from the far short wall; USB on the side.',
|
||||
'Narrow and deep: fits beside other gear on a crowded desk or pedalboard.']};
|
||||
}
|
||||
/* Landscape: lid L wide x W deep, three zones left to right, jacks and USB on the back long wall. */
|
||||
function layoutL(W,L,H,E,P){
|
||||
var A=L, B=W, m=E+3, items=[], scr={x:m,y:m,w:44,h:44};
|
||||
var encY=scr.y+scr.h+P.K/2+3;
|
||||
items.push({name:'Encoder',x:scr.x+P.K/2+1,y:encY,hole:7,clr:P.K/2,kind:'knob'});
|
||||
var bc=P.BN/2, px=Math.min(20,(A-m-(scr.x+scr.w)-Math.max(P.SN,P.K)-6-2*bc)/3), py=Math.min(20,(B-2*m-2*bc)/2);
|
||||
var bx=A-m-bc-1.5*px, by=B/2;
|
||||
buttons(items,bx,by,px,py,P);
|
||||
var midL=scr.x+scr.w, midR=bx-1.5*px-bc, cx=(midL+midR)/2, ys=function(i){ return m+(B-2*m)*(i+0.5)/3; };
|
||||
items.push({name:'ENERGY pot',x:cx,y:ys(0),hole:7,clr:P.K/2,kind:'knob'});
|
||||
items.push({name:'DRIFT pot',x:cx,y:ys(1),hole:7,clr:P.K/2,kind:'knob'});
|
||||
items.push({name:'SEED',x:cx,y:ys(2),hole:P.SH,clr:P.SN/2,kind:'seed'});
|
||||
var jacks=[], jn=['MIDI IN','OUT · KIT','OUT · SYNTH'];
|
||||
var jc=Math.max(5,P.D/2+2), jp=Math.max(16,2*jc+4);
|
||||
for(var j=0;j<3;j++) jacks.push({name:jn[j],x:A-m-jc-2-(2-j)*jp,z:H/2,hole:P.D,clr:jc});
|
||||
return {A:A,B:B,m:m,scr:scr,items:items,jacks:jacks,usbBack:{x:m+38,z:16,w:12,h:8},usbSide:null,wire:{x:scr.x+5,y:scr.y+22,hole:8},px:px,py:py,
|
||||
why:['Three zones left to right: screen with the encoder, then ENERGY / DRIFT / SEED in a column, then the 4 × 3 block: 11 track buttons with the RUN toggle in the top-right corner.',
|
||||
'The button block has the same shape as the wiring matrix and the screen map, so the LCD map sits right next to the buttons it mirrors.',
|
||||
'Everything is one hand-reach from the front edge, and the screen is never under your hands.',
|
||||
'All cables and USB leave from the back long wall. Wider and shallower: sits in front of a keyboard or sampler.']};
|
||||
}
|
||||
function layout(W,L,H,E,P){ return ORIENT==='L' ? layoutL(W,L,H,E,P) : layoutP(W,L,H,E,P); }
|
||||
|
||||
function check(W,L,H,E,Lo){
|
||||
W=Lo.A; L=Lo.B;
|
||||
var out=[], it=Lo.items, i, j;
|
||||
function inRect(a,R,pad){ var dx=Math.max(R.x-a.x,0,a.x-(R.x+R.w)), dy=Math.max(R.y-a.y,0,a.y-(R.y+R.h)); return Math.hypot(dx,dy)<a.clr+pad; }
|
||||
for(i=0;i<it.length;i++){
|
||||
var a=it[i];
|
||||
if(a.x-a.clr<E || a.x+a.clr>W-E || a.y-a.clr<E || a.y+a.clr>L-E){ a.bad=true; out.push(a.name+' runs past the usable lid area.'); }
|
||||
if(inRect(a,Lo.scr,1.5)){ a.bad=true; out.push(a.name+' overlaps the screen module.'); }
|
||||
for(j=i+1;j<it.length;j++){ var b=it[j], d=Math.hypot(a.x-b.x,a.y-b.y);
|
||||
if(d<a.clr+b.clr+1){ a.bad=b.bad=true; out.push(a.name+' and '+b.name+' are too close ('+r1(d)+' mm apart, need '+r1(a.clr+b.clr+1)+').'); } }
|
||||
}
|
||||
var J=Lo.jacks;
|
||||
for(i=0;i<J.length;i++){ if(J[i].z-J[i].clr<E || J[i].z+J[i].clr>H-E){ J[i].bad=true; out.push('The box is too shallow for the MIDI jacks (they need about '+r1(2*J[i].clr+2*E)+' mm of wall height).'); break; } }
|
||||
if(Lo.usbBack && J[0].x-12<Lo.usbBack.x+Lo.usbBack.w/2){ out.push('The USB slot runs into the MIDI IN jack.'); }
|
||||
for(i=0;i<J.length-1;i++) if(J[i+1].x-J[i].x<J[i].clr+J[i+1].clr+2){ J[i].bad=J[i+1].bad=true; out.push('MIDI jacks are too close; their nuts will clash.'); break; }
|
||||
if(ORIENT==='P') out.push('Portrait does not match the stripboard: its USB edge needs the long back wall. Use Landscape for this build.');
|
||||
if(Lo.px<15) out.push('Button pitch is '+r1(Lo.px)+' mm, tight for fingers. 16 mm or more plays better.');
|
||||
return out;
|
||||
}
|
||||
|
||||
function draw(){
|
||||
var W=v('W'), L=v('L'), H=v('H'), E=v('E'), P={BH:v('BH'),BN:v('BN'),SH:v('SH'),SN:v('SN'),K:v('K'),D:v('D')};
|
||||
try{ var s={}; ids.forEach(function(k){ s[k]=document.getElementById(k).value; }); localStorage.setItem('seedbox.panel',JSON.stringify(s)); }catch(e){}
|
||||
var Lo=layout(W,L,H,E,P), warns=check(W,L,H,E,Lo);
|
||||
var C={ink:css('--ink'),muted:css('--muted'),line:css('--line'),accent:css('--accent'),warn:css('--warn'),metal:css('--metal'),edge:css('--metal-edge'),print:css('--print'),pcb:css('--pcb'),lcd:css('--lcd'),font:css('--f-mono')};
|
||||
svg.innerHTML='';
|
||||
var LW=Lo.A, LD=Lo.B, pad=14, gap=8, ox=pad+12, oyEnd=pad+8, oyLid=oyEnd+H+gap, oxSide=ox+LW+gap, VW=oxSide+(Lo.usbSide?H:0)+pad+6, VH=oyLid+LD+pad+10;
|
||||
svg.setAttribute('viewBox','0 0 '+VW+' '+VH);
|
||||
var g=el('g',{'font-family':C.font,'font-size':'2.6'});
|
||||
function panel(x,y,w,h,label){ el('rect',{x:x,y:y,width:w,height:h,rx:1.5,fill:C.metal,stroke:C.edge,'stroke-width':0.5},g); el('text',{x:x,y:y-2.2,fill:C.muted,'font-size':'2.8'},g,label); }
|
||||
function dim(x1,y1,x2,y2,txt,vert){
|
||||
el('line',{x1:x1,y1:y1,x2:x2,y2:y2,stroke:C.muted,'stroke-width':0.25},g);
|
||||
[[x1,y1],[x2,y2]].forEach(function(p){ var t=vert?{x1:p[0]-1.5,y1:p[1],x2:p[0]+1.5,y2:p[1]}:{x1:p[0],y1:p[1]-1.5,x2:p[0],y2:p[1]+1.5}; t.stroke=C.muted; t['stroke-width']=0.25; el('line',t,g); });
|
||||
var t=el('text',{x:(x1+x2)/2+(vert?-1.6:0),y:(y1+y2)/2+(vert?0:-1.2),fill:C.muted,'text-anchor':'middle','font-size':'2.6'},g,txt);
|
||||
if(vert) t.setAttribute('transform','rotate(-90 '+((x1+x2)/2-1.6)+' '+((y1+y2)/2)+')');
|
||||
}
|
||||
function hole(cx,cy,d,clr,bad,kind,label,below){
|
||||
if(kind==='knob'){ el('circle',{cx:cx,cy:cy,r:clr,fill:'#15181C',stroke:bad?C.warn:'#4A525B','stroke-width':bad?0.6:0.4},g); el('line',{x1:cx,y1:cy-clr+1.2,x2:cx,y2:cy-clr*0.35,stroke:'#D9DEE3','stroke-width':0.8},g); }
|
||||
else if(kind==='toggle'){ el('circle',{cx:cx,cy:cy,r:clr,fill:'#2C3238',stroke:bad?C.warn:'#4A525B','stroke-width':0.4},g); el('rect',{x:cx-1,y:cy-6,width:2,height:6,rx:1,fill:'#C9CFD5'},g); }
|
||||
else { el('circle',{cx:cx,cy:cy,r:clr,fill:'#2C3238',stroke:bad?C.warn:'#4A525B','stroke-width':bad?0.6:0.4},g); el('circle',{cx:cx,cy:cy,r:d/2-0.6,fill:kind==='seed'?'#3B4148':'#30363D',stroke:'#14171A','stroke-width':0.3},g); }
|
||||
el('circle',{cx:cx,cy:cy,r:0.5,fill:C.accent},g);
|
||||
if(label) el('text',{x:cx,y:below?cy+clr+3:cy-clr-1.2,fill:C.print,'text-anchor':'middle','font-size':'2.4','letter-spacing':'0.1'},g,label);
|
||||
}
|
||||
// end wall (jacks)
|
||||
panel(ox,oyEnd,LW,H,ORIENT==='L'?'BACK WALL · cables':'END WALL · cables');
|
||||
Lo.jacks.forEach(function(j){ var cx=ox+j.x, cy=oyEnd+j.z;
|
||||
hole(cx,cy,j.hole,j.clr,j.bad,'jack'); el('text',{x:cx,y:oyEnd+H-2,fill:C.print,'text-anchor':'middle','font-size':'2.2'},g,j.name); });
|
||||
// lid
|
||||
panel(ox,oyLid,LW,LD,'LID · top view · you sit at the bottom edge');
|
||||
el('rect',{x:ox+E,y:oyLid+E,width:LW-2*E,height:LD-2*E,fill:'none',stroke:'#3A4047','stroke-width':0.3,'stroke-dasharray':'1.2 1'},g);
|
||||
var S=Lo.scr, sx=ox+S.x, sy=oyLid+S.y;
|
||||
el('rect',{x:sx,y:sy,width:S.w,height:S.h,rx:1,fill:C.pcb},g);
|
||||
[[2.5,2.5],[S.w-2.5,2.5],[2.5,S.h-2.5],[S.w-2.5,S.h-2.5]].forEach(function(p){ el('circle',{cx:sx+p[0],cy:sy+p[1],r:1.3,fill:'#7A1416'},g); });
|
||||
el('rect',{x:sx+5,y:sy+2.5,width:33,height:36,rx:0.8,fill:'#A9B0B6'},g);
|
||||
el('rect',{x:sx+7,y:sy+5,width:26,height:30,rx:0.6,fill:C.lcd},g);
|
||||
for(var p=0;p<8;p++) el('circle',{cx:sx+2.5,cy:sy+12+p*2.54,r:0.55,fill:'#E9C46A'},g);
|
||||
// a hint of the 4x3 track map on the LCD
|
||||
for(var r=0;r<3;r++) for(var c=0;c<4;c++) el('rect',{x:sx+8.5+c*6,y:sy+20+r*4.6,width:5,height:3.6,fill:(r+c)%3?'#2A3330':'none',stroke:'#2A3330','stroke-width':0.3},g);
|
||||
el('circle',{cx:ox+Lo.wire.x,cy:oyLid+Lo.wire.y,r:Lo.wire.hole/2,fill:'none',stroke:C.accent,'stroke-width':0.35,'stroke-dasharray':'0.8 0.6'},g);
|
||||
Lo.items.forEach(function(it){
|
||||
var lab=it.kind==='knob'||it.kind==='seed'||it.kind==='toggle'?it.name.replace(' pot','').replace(' toggle',''):it.name;
|
||||
hole(ox+it.x,oyLid+it.y,it.hole,it.clr,it.bad,it.kind==='fill'?'btn':it.kind,lab,true);
|
||||
});
|
||||
// USB slot: side wall (portrait) or back wall (landscape)
|
||||
if(Lo.usbSide){
|
||||
panel(oxSide,oyLid,H,LD,'SIDE');
|
||||
var U=Lo.usbSide; el('rect',{x:oxSide+H-U.z-U.h/2,y:oyLid+U.y-U.w/2,width:U.h,height:U.w,rx:1,fill:'#0A0C0E',stroke:C.accent,'stroke-width':0.35},g);
|
||||
el('text',{x:oxSide+H/2,y:oyLid+U.y+U.w/2+4,fill:C.print,'text-anchor':'middle','font-size':'2.4'},g,'USB');
|
||||
el('text',{x:oxSide+H/2,y:oyLid+LD-3,fill:C.muted,'text-anchor':'middle','font-size':'2.2'},g,'floor →');
|
||||
} else {
|
||||
var Ub=Lo.usbBack; el('rect',{x:ox+Ub.x-Ub.w/2,y:oyEnd+H-Ub.z-Ub.h/2,width:Ub.w,height:Ub.h,rx:1,fill:'#0A0C0E',stroke:C.accent,'stroke-width':0.35},g);
|
||||
el('text',{x:ox+Ub.x,y:oyEnd+H-2,fill:C.print,'text-anchor':'middle','font-size':'2.2'},g,'USB');
|
||||
}
|
||||
// dimensions
|
||||
dim(ox,oyLid+LD+6,ox+LW,oyLid+LD+6,LW+' mm');
|
||||
dim(ox-6,oyLid,ox-6,oyLid+LD,LD+' mm',true);
|
||||
dim(ox-6,oyEnd,ox-6,oyEnd+H,H+' mm',true);
|
||||
// fit check
|
||||
var wb=document.getElementById('warns'); wb.innerHTML='';
|
||||
if(!warns.length){ var d0=document.createElement('div'); d0.className='ok'; d0.textContent='Everything fits. Button pitch '+r1(Lo.px)+' × '+r1(Lo.py)+' mm.'; wb.appendChild(d0); }
|
||||
warns.filter(function(w,i,a){ return a.indexOf(w)===i; }).forEach(function(w){ var d=document.createElement('div'); d.className='bad'; d.textContent=w; wb.appendChild(d); });
|
||||
// drill table
|
||||
var tb=document.getElementById('drill'); tb.innerHTML='';
|
||||
function row(where,part,x,y,h){ var tr=document.createElement('tr'); [where,part,x,y,h].forEach(function(t,i){ var td=document.createElement('td'); if(i>=2) td.className='n'; td.textContent=t; tr.appendChild(td); }); tb.appendChild(tr); }
|
||||
row('Lid','Screen wire hole (under the header)',r1(Lo.wire.x),r1(Lo.wire.y),'Ø '+Lo.wire.hole);
|
||||
Lo.items.forEach(function(it){ row('Lid',it.name,r1(it.x),r1(it.y),'Ø '+it.hole); });
|
||||
var wn=ORIENT==='L'?'Back wall':'End wall';
|
||||
Lo.jacks.forEach(function(j){ row(wn,'Jack '+j.name,r1(j.x),r1(j.z)+' down','Ø '+j.hole); });
|
||||
if(Lo.usbSide) row('Side wall','USB slot (Pico port)',r1(Lo.usbSide.z)+' up from inside floor',r1(Lo.usbSide.y),Lo.usbSide.w+' × '+Lo.usbSide.h+' slot');
|
||||
else row(wn,'USB slot (Pico port)',r1(Lo.usbBack.x),r1(Lo.usbBack.z)+' up from inside floor',Lo.usbBack.w+' × '+Lo.usbBack.h+' slot');
|
||||
var wy=document.getElementById('why'); wy.innerHTML=''; Lo.why.forEach(function(t){ var li=document.createElement('li'); li.textContent=t; wy.appendChild(li); });
|
||||
document.getElementById('oP').setAttribute('aria-pressed',String(ORIENT==='P')); document.getElementById('oL').setAttribute('aria-pressed',String(ORIENT==='L'));
|
||||
}
|
||||
ids.forEach(function(k){ document.getElementById(k).addEventListener('input',draw); });
|
||||
try{ ORIENT=localStorage.getItem('seedbox.orient')==='P'?'P':'L'; }catch(e){}
|
||||
[['oP','P'],['oL','L']].forEach(function(b){ document.getElementById(b[0]).addEventListener('click',function(){ ORIENT=b[1]; try{ localStorage.setItem('seedbox.orient',ORIENT); }catch(e){} draw(); }); });
|
||||
draw();
|
||||
})();
|
||||
</script>
|
||||
111
tools/wiring-src/body.html
Normal file
111
tools/wiring-src/body.html
Normal file
@@ -0,0 +1,111 @@
|
||||
<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>
|
||||
57
tools/wiring-src/head.html
Normal file
57
tools/wiring-src/head.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<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>
|
||||
246
tools/wiring-src/render.js
Normal file
246
tools/wiring-src/render.js
Normal file
@@ -0,0 +1,246 @@
|
||||
(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){}
|
||||
})();
|
||||
501
tools/wiring.html
Normal file
501
tools/wiring.html
Normal file
@@ -0,0 +1,501 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user