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:
93
hardware/check-layout.js
Normal file
93
hardware/check-layout.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Checks hardware/layout.js: geometry (on board, not on a cut, one lead per hole),
|
||||
footprints (nothing parked under the Pico or the DIP socket that should not be),
|
||||
and the electrical rules of the circuit (MIDI 1.0 3.3 V spec CA-033, TRS Type A jacks, Pico pin map).
|
||||
Usage: node hardware/check-layout.js [--nets] exit code 0 = clean. */
|
||||
var L=require('./layout.js');
|
||||
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); }
|
||||
var key=function(h){ return h[0]+','+h[1]; };
|
||||
var cut={}; L.cuts.forEach(function(h){ cut[key(h)]=1; });
|
||||
var issues=[];
|
||||
|
||||
// --- copper: each uncut run of a strip is a segment; jumpers join segments ---
|
||||
function seg(h){ if(cut[key(h)]) return null; var a=h[0], b=h[0];
|
||||
while(a>0 && !cut[key([a-1,h[1]])]) a--; while(b<L.W-1 && !cut[key([b+1,h[1]])]) b++; return 'r'+h[1]+':'+a+'-'+b; }
|
||||
var par={}; function find(x){ while(par[x]!==undefined && par[x]!==x) x=par[x]; return x; }
|
||||
function join(a,b){ a=find(a); b=find(b); if(a!==b) par[a]=b; }
|
||||
|
||||
// --- every lead and wire end, by name ---
|
||||
var occ={}, ends={}; // ends: name -> hole
|
||||
function use(h,what){
|
||||
if(h[0]<0||h[1]<0||h[0]>=L.W||h[1]>=L.H) issues.push('off the board: '+what+' at '+pos(h));
|
||||
if(cut[key(h)]) issues.push('on a cut: '+what+' at '+pos(h));
|
||||
(occ[key(h)]=occ[key(h)]||[]).push(what);
|
||||
}
|
||||
L.parts.forEach(function(p){ p.pins.forEach(function(pin,i){
|
||||
var nm = p.id==='pico' ? 'pico.'+pin.num+'.'+pin.n : p.id+'.'+(i+1);
|
||||
if(p.id==='U1') nm='U1.'+pin.n.split(' ')[0];
|
||||
if(p.id==='D1') nm='D1.'+pin.n;
|
||||
use(pin.h,nm); ends[nm]=pin.h; }); });
|
||||
L.jumpers.forEach(function(j,i){ use(j.a,'J'+(i+1)+'a'); use(j.b,'J'+(i+1)+'b'); var a=seg(j.a), b=seg(j.b); if(a&&b) join(a,b); });
|
||||
// the Pico's GND pins are one plane inside the module
|
||||
var gnds=L.parts[0].pins.filter(function(p){ return p.n==='GND'; }).map(function(p){ return seg(p.h); }).filter(Boolean);
|
||||
for(var gi=1;gi<gnds.length;gi++) join(gnds[0],gnds[gi]);
|
||||
L.wires.forEach(function(w,i){ var nm='P'+(i+1)+' '+w.to; use(w.h,nm); ends['P'+(i+1)]=w.h; ends[w.to]=w.h; });
|
||||
Object.keys(occ).forEach(function(k){ if(occ[k].length>1) issues.push('two leads in one hole '+pos(k.split(',').map(Number))+': '+occ[k].join(' & ')); });
|
||||
|
||||
function net(name){ var h=ends[name]; if(!h){ issues.push('rule names an unknown end: '+name); return '?'+name; } var s=seg(h); return s?find(s):'cut'; }
|
||||
function picoPin(sig){ var p=L.parts[0].pins.filter(function(x){ return x.n===sig; })[0]; return p?'pico.'+p.num+'.'+p.n:'?'; }
|
||||
function same(a,b,why){ if(net(a)!==net(b)) issues.push('not connected: '+a+' and '+b+' ('+why+')'); }
|
||||
function differ(a,b,why){ if(net(a)===net(b)) issues.push('shorted: '+a+' and '+b+' ('+why+')'); }
|
||||
function bridge(part,a,b,why){ var n1=net(part+'.1'), n2=net(part+'.2'), A=net(a), B=net(b);
|
||||
if(!((n1===A&&n2===B)||(n1===B&&n2===A))) issues.push(part+' should sit between '+a+' and '+b+' ('+why+')'); }
|
||||
|
||||
// --- footprints: keep-out zones ---
|
||||
var pico=L.parts[0].pins, rows=pico.map(function(p){ return p.h[1]; });
|
||||
var pr0=Math.min.apply(null,rows), pr1=Math.max.apply(null,rows), pc0=12, pc1=19;
|
||||
L.parts.forEach(function(p){ if(p.id==='pico') return; p.pins.forEach(function(pin){
|
||||
if(pin.h[1]>=pr0-1 && pin.h[1]<=pr1+1 && pin.h[0]>=pc0 && pin.h[0]<=pc1) issues.push(p.id+' lead at '+pos(pin.h)+' is inside the Pico footprint'); }); });
|
||||
var u=L.parts.filter(function(p){ return p.id==='U1'; })[0].pins.map(function(p){ return p.h; });
|
||||
var uc0=Math.min.apply(null,u.map(function(h){return h[0];})), uc1=Math.max.apply(null,u.map(function(h){return h[0];}));
|
||||
var ur0=Math.min.apply(null,u.map(function(h){return h[1];})), ur1=Math.max.apply(null,u.map(function(h){return h[1];}));
|
||||
L.parts.forEach(function(p){ if(p.id==='U1'||p.id==='pico') return; p.pins.forEach(function(pin){
|
||||
if(pin.h[0]>uc0 && pin.h[0]<uc1 && pin.h[1]>=ur0 && pin.h[1]<=ur1) issues.push(p.id+' lead at '+pos(pin.h)+' is under the DIP-8 socket'); }); });
|
||||
|
||||
// --- the circuit ---
|
||||
var VBUS=picoPin('VBUS'), V33=picoPin('3V3'), GND=picoPin('GND'), AGND=picoPin('AGND');
|
||||
// rails
|
||||
differ(VBUS,V33,'5 V vs 3.3 V'); differ(VBUS,GND,'5 V vs GND'); differ(V33,GND,'3.3 V vs GND');
|
||||
// MIDI IN: pin 4 -> 220R -> opto anode; pin 5 -> opto cathode; 1N4148 reverse across the LED
|
||||
bridge('R1','MIDI IN jack · ring (pin 4)','U1.2','MIDI IN current limit');
|
||||
same('MIDI IN jack · tip (pin 5)','U1.3','MIDI IN pin 5 to opto cathode');
|
||||
same('D1.K','U1.2','diode cathode on the LED anode'); same('D1.A','U1.3','diode anode on the LED cathode');
|
||||
same('U1.8',VBUS,'6N137 VCC from 5 V'); same('U1.5',GND,'6N137 GND');
|
||||
same('U1.6',picoPin('GP1'),'opto out to GP1 (UART0 RX)'); bridge('R2','U1.6',V33,'1k pull-up to 3.3 V');
|
||||
same('U1.7',VBUS,'6N137 enable (VE) high, or the output stays off'); bridge('C1',VBUS,GND,'100 nF supply bypass, required for the 6N137');
|
||||
// MIDI OUTs: TX -> 10R -> pin 5, 3V3 -> 33R -> pin 4, pin 2 -> GND
|
||||
bridge('R4',picoPin('GP0'),'OUT KIT jack · tip (pin 5)','10 Ω on TX (UART0)');
|
||||
bridge('R5',V33,'OUT KIT jack · ring (pin 4)','33 Ω on 3.3 V');
|
||||
same('OUT KIT jack · sleeve (pin 2)',GND,'shield');
|
||||
bridge('R6',picoPin('GP4'),'OUT SYNTH jack · tip (pin 5)','10 Ω on TX (UART1)');
|
||||
bridge('R7',V33,'OUT SYNTH jack · ring (pin 4)','33 Ω on 3.3 V');
|
||||
same('OUT SYNTH jack · sleeve (pin 2)',GND,'shield');
|
||||
// panel
|
||||
var SIG={'LCD · RST (pin 4)':'GP21','LCD · SCE (pin 3)':'GP17','LCD · D/C (pin 5)':'GP20','LCD · DN/MOSI (pin 6)':'GP19','LCD · SCLK (pin 7)':'GP18',
|
||||
'Encoder · SIA':'GP10','Encoder · SIB':'GP11','Encoder · SW':'GP12','SEED · one lug':'GP13','RUN toggle · middle lug':'GP14',
|
||||
'ENERGY pot · middle lug':'GP26','DRIFT pot · middle lug':'GP27',
|
||||
'Matrix row 1 (KICK, KICK 2, SNARE)':'GP2','Matrix row 2 (HAT, HAT 16, PERC, GHOST)':'GP3','Matrix row 3 (BASS, MEL A, MEL B, PAD)':'GP5',
|
||||
'Matrix column 1 (KICK, HAT, BASS)':'GP6','Matrix column 2 (KICK 2, HAT 16, MEL A)':'GP7','Matrix column 3 (SNARE, PERC, MEL B)':'GP8','Matrix column 4 (GHOST, PAD)':'GP9'};
|
||||
Object.keys(SIG).forEach(function(w){ same(w,picoPin(SIG[w]),'pin map'); });
|
||||
['LCD · VCC (pin 1)','LCD · LED (pin 8) through 330 Ω','Encoder · VCC','Pots · right lugs (chain both)'].forEach(function(w){ same(w,V33,'3.3 V supply'); });
|
||||
['LCD · GND (pin 2)','Encoder · GND','Panel ground bus · SEED other lug, RUN one outer lug'].forEach(function(w){ same(w,GND,'ground'); });
|
||||
same('Pots · left lugs (chain both)',AGND,'pots on analog ground');
|
||||
// every signal net is distinct from every other
|
||||
var sigs=Object.keys(SIG).map(function(w){ return picoPin(SIG[w]); }).concat([picoPin('GP0'),picoPin('GP1'),picoPin('GP4')]);
|
||||
for(var i=0;i<sigs.length;i++) for(var j=i+1;j<sigs.length;j++) if(net(sigs[i])===net(sigs[j])) issues.push('shorted: '+sigs[i]+' and '+sigs[j]);
|
||||
sigs.forEach(function(s){ [VBUS,V33,GND].forEach(function(r){ if(net(s)===net(r)) issues.push('shorted to a rail: '+s+' and '+r); }); });
|
||||
|
||||
if(process.argv.indexOf('--nets')>=0){ var nets={};
|
||||
Object.keys(ends).forEach(function(n){ if(/^P\d+$/.test(n)) return; (nets[net(n)]=nets[net(n)]||[]).push(n+' @'+pos(ends[n])); });
|
||||
Object.keys(nets).sort().forEach(function(k){ console.log(k); nets[k].forEach(function(x){ console.log(' '+x); }); }); }
|
||||
console.log(L.parts.length+' parts, '+L.jumpers.length+' links, '+L.wires.length+' wire pads, '+L.cuts.length+' cut holes');
|
||||
if(issues.length){ console.log(issues.length+' issue(s):'); issues.forEach(function(x){ console.log(' - '+x); }); process.exit(1); }
|
||||
console.log('layout clean');
|
||||
84
hardware/layout.js
Normal file
84
hardware/layout.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/* 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;
|
||||
Reference in New Issue
Block a user