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

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

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

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

244 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<title>Seedbox 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>