Hako Devices

Build from parts

A description names one machine. Give a board with what goes on it, or a base (a whole device by its catalogue key) with what goes on that.

import { build } from "@johnmorrisdotca/hako-devices/build";

const machine = build({ board: "pi4", case: "armor", hat: "pitft-28" });
machine.report.stack; // { riser: 0, under: 16.8 }
machine.size; // the mesh's width, depth and height in millimetres
machine.glb().bytes; // a .glb
machine.svg("thin"); // the drawing
machine.metrics(); // its scale, for drawing it to scale
machine.printAreas(); // where `print` can go

build throws a FitError when a machine does not go together; its report says why. check answers the same question with no three.js, so a form can ask on every change:

import { check } from "@johnmorrisdotca/hako-devices";

const report = check({ board: "pi3b", hat: "poe-plus" });
report.ok; // false
report.issues[0].code; // "no-poe-pins"

A form that never shows an error

whatFits says what to offer and nearestWorking what to change, so a form never has to show a fault. Both are pure and need no three.js, and neither touches print, which stays at the person's own risk.

import { fillNeeded, nearestWorking, whatFits } from "@johnmorrisdotca/hako-devices";

// a needed choice with one answer is made for you: bare holes under a hat get their header, and a module with no hat gets the one hat that takes it
fillNeeded({ board: "pi-zero-2w", hat: "oled-bonnet" }).description; // { board: "pi-zero-2w", hat: "oled-bonnet", solderHeader: true }

// grey what cannot work, with its reason
whatFits({ board: "pi-zero-2w" }, "case").find((option) => option.id === "armor"); // { id: "armor", name: "...", fits: false, why: "... is cut for other sockets than those of ..." }

// or take anything, and change the rest: the second argument is the choice just made, which stays
const { description, changes } = nearestWorking({ board: "pi-zero-2w", case: "armor" }, "board");
description.case; // "unipi"
changes[0].message; // "the case is now Small domed case (UniPiCase Zero style)"

The nearest set changes as little as it can. Another case that fits is nearer than no case, because cases do one job; a hat, a module or a cooler each does its own job, so no hat is nearer than a different hat. A board moves last of all, when a part fits no other way. hat-overhangs stays a warning: a hat past a board's edge goes together, so it is a fact to say, not a fit to avoid. The demo's builder is both ways behind one switch.

The description

FieldWhat
boardA board: pi1b-plus, pi2b, pi3b, pi3b-plus, pi3a-plus, pi4, pi5, pi-zero-w, pi-zero-2w, cm4-io
casearmor (finned, keeps the header reachable; fits the USB-C full-size board), argon (faceted, closed; same board), unipi (small domed, closed; the small boards), plate (a plain plate on standoffs; any board)
hatpitft-28, pitft-35, oled-bonnet, epaper-213, poe-plus, proto-hat
screenoled-module (on a hat with a display header: proto-hat), touch-7 (holds the board on its back)
cooleractive-cooler (the fan and fins for pi5)
riserMillimetres of stacking header under the hat. Left out: as much as it takes to clear what is under the hat
solderHeaderPuts the pins on a board that comes with bare header holes
baseA whole device by its catalogue key, instead of a board
standUnder a tablet or a phone: desk-stand, or null to lie flat. Left out: the desk stand
drivesThe drives in a NAS's bays, in order: hdd-35, ssd-25, or null for an empty bay. Left out: all full
printYour own words, at your own risk: one string for the device's main area, or by area ({ side: "Backup" })
turn0 or 180: which way round it stands. Left out: the way its hat reads best

How parts attach

MountWhat attachesChecked
40-pin headerA hatThe board has soldered pins; a closed case does not cover them; the hat's outline clears everything tall under it, by real heights
Four network power pinsA hat that needs themThe board has them
Sockets and outlineA caseThe case is cut for this board's sockets, and the board fits inside it
Display headerA screen moduleThe hat has one
Standoffs on a display's backA boardIts screw holes line up
Cooler holesA coolerMade for this board; not inside a closed case
Drive bayA driveIts size goes in the bay; no more drives than bays
Foot of a slabA standIt holds a slab of that width

A hat over tall sockets rises by itself: check({ board: "pi4", hat: "pitft-28" }) gives a note that it sits 6.2 mm higher on a stacking header. Give riser yourself and a height that is too short is a warning that names the height needed.

The fit codes

CodeLevelMeans
nothing, board-and-baseerrorGive a board or a base, one of them
unknown-parterrorNo part has that name
case-wrong-board, case-too-smallerrorThe case is cut for other sockets, or the board is bigger than its inside
case-covers-headererrorThe case closes over the header, so a hat cannot reach it
case-on-screenerrorA display that holds the board takes no case
header-not-solderederrorBare holes: set solderHeader
no-poe-pinserrorThe hat needs the four pins
hat-overhangswarningThe hat reaches past the board's edge
hat-meets-partwarningThe given riser is too short
hat-raisednoteThe hat was raised to clear something, by how much
riser-too-tall, riser-without-haterror0 to 30 mm, and under a hat
cooler-wrong-board, cooler-in-closed-caseerrorIt does not go there
screen-needs-header, screen-wrong-board, two-screenserrorIt does not go there
stand-wrong-base, stand-wrong-sizeerrorNot a slab, or too wide or too narrow for the stand
drives-wrong-base, too-many-drives, drive-wrong-bayerrorNo bays, too many drives, or a drive that does not go in
part-on-baseerrorA hat on a whole device, or a stand on a board

Presets

PRESETS holds real machines to use or start from: a Pi 4 in a finned case with a 2.8-inch touch screen, a Pi 3 B with an OLED bonnet, a Pi Zero 2 W in a small domed case, and more.

import { PRESETS } from "@johnmorrisdotca/hako-devices";
import { build } from "@johnmorrisdotca/hako-devices/build";

const machines = PRESETS.map((preset) => build(preset.description));
machines.length; // 8

A part on its own

import { buildPart } from "@johnmorrisdotca/hako-devices/build";

buildPart("armor").svg("thick");
buildPart("pitft-28").glb();

The print option

Every device has areas where your words can go: machine.printAreas() lists them, the first being the main one. Nothing is painted unless you ask, and a shipped .glb has clear faces there, so nothing shows in any other viewer. What you print is at your own risk: do not print someone else's trademark.