Compare commits
8 Commits
v0.3.0-bet
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2c37095ef | ||
|
|
6f9be6a146 | ||
|
|
f0ea6211d1 | ||
|
|
1a9f078829 | ||
|
|
feccdc668c | ||
|
|
f8c1ccc756 | ||
|
|
7a57d3ecc6 | ||
|
|
63cb9fd908 |
@ -12,7 +12,7 @@ The following things were substantially improved or added:
|
||||
- All Brother lasers are supported (even host-based). PCL/PS remains the recommended way to print. HBP (host based printing) is capped to 600dpi (memory limit of pi zero)
|
||||
All other brands that support true PCL or PostScript should work too. See: [printers.md](docs/printers.md)
|
||||
- Print output can be sent non-inverted and non-mirrored for checking before printing to transfer paper.
|
||||
- A new method (SeedEtcher Transfer Stack<sup>TM</sup>) leverages the use of silicone sheets to reliably transfer toner masks to both sides of a metal plate at once.
|
||||
- A new method (SeedEtcher Transfer Stack™) leverages the use of silicone sheets to reliably transfer toner masks to both sides of a metal plate at once.
|
||||
This means, you can also etch both sides at once!
|
||||
- A new plate layout design optimizes for etching. All rounded forms, including a custom designed font face and QRs with circle modules. Also the mask area now covers the whole plate except for the side where you tape it for transfer. This means you only need to tape one side before etching.
|
||||
- I designed a 3d printable etching container for optimal etching performance. No manual movement required. It will be released after geyser.io campaign, presumably.
|
||||
@ -94,6 +94,12 @@ Run controller on Pi:
|
||||
Licensed under Apache License 2.0: [LICENSE](LICENSE) and [NOTICE](NOTICE)
|
||||
Third-party component licenses: [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md)
|
||||
|
||||
SeedEtcher™ and SeedEtcher Transfer Stack™ are trademarks of the SeedEtcher project.
|
||||
|
||||
The Apache License 2.0 applies only to the code, documentation, and design
|
||||
files in this repository and does not grant rights to use the SeedEtcher
|
||||
name, logos, or branding.
|
||||
|
||||
---
|
||||
|
||||
## Legacy
|
||||
|
||||
66
TRADEMARK.md
Normal file
66
TRADEMARK.md
Normal file
@ -0,0 +1,66 @@
|
||||
# SeedEtcher Trademark Policy
|
||||
|
||||
SeedEtcher™ and SeedEtcher Transfer Stack™ are names and brands of the SeedEtcher project.
|
||||
|
||||
The SeedEtcher name, SeedEtcher Transfer Stack name, logos, wordmarks,
|
||||
branding, website identity, and other project identifiers are used to
|
||||
identify the official project, documentation, releases, and hardware kits.
|
||||
|
||||
No trademark rights are granted under the Apache License 2.0. That license
|
||||
applies only to the code, design files, and documentation contained in this
|
||||
repository.
|
||||
|
||||
## Permitted Use
|
||||
|
||||
You may:
|
||||
|
||||
- Use the SeedEtcher name to refer to the project truthfully.
|
||||
- Link to the official SeedEtcher repository, website, or documentation.
|
||||
- State that a product, fork, or derivative is compatible with or based on
|
||||
SeedEtcher, so long as this does not imply endorsement.
|
||||
|
||||
Examples:
|
||||
|
||||
- "Compatible with SeedEtcher"
|
||||
- "Based on the SeedEtcher project"
|
||||
|
||||
## Restricted Use
|
||||
|
||||
You may NOT:
|
||||
|
||||
- Sell products using the SeedEtcher name.
|
||||
- Distribute hardware kits branded as SeedEtcher.
|
||||
- Use the SeedEtcher logo, wordmark, or other branding.
|
||||
- Use the SeedEtcher name in a way that implies endorsement, affiliation,
|
||||
sponsorship, certification, approval, or official status.
|
||||
- Use confusingly similar names, branding, domains, social media handles,
|
||||
storefronts, or campaign pages that could cause users to believe a product
|
||||
or service is official SeedEtcher.
|
||||
- Present a fork or derivative as the official SeedEtcher project.
|
||||
|
||||
## Forks and Derivatives
|
||||
|
||||
Forks and derivatives are permitted under the Apache License 2.0, but they
|
||||
must use a different name and different branding unless explicit permission
|
||||
is granted.
|
||||
|
||||
Example:
|
||||
|
||||
- A fork of SeedEtcher must use its own product name and branding.
|
||||
|
||||
## Official Products
|
||||
|
||||
Only products distributed by the SeedEtcher project or by explicitly
|
||||
authorized sellers may use the SeedEtcher name or logo.
|
||||
|
||||
## Permission Requests
|
||||
|
||||
For permission requests regarding use of the SeedEtcher trademark, open an
|
||||
issue in the project repository.
|
||||
|
||||
## Changes to This Policy
|
||||
|
||||
This policy may be updated from time to time. Continued use of the
|
||||
SeedEtcher name or branding must comply with the latest published version.
|
||||
|
||||
SeedEtcher™ is a trademark of the SeedEtcher project.
|
||||
@ -27,6 +27,9 @@ type Decoder struct {
|
||||
}
|
||||
|
||||
func Encode(message []byte, seqNum, seqLen int) []byte {
|
||||
if seqLen <= 0 {
|
||||
panic("seqLen out of range")
|
||||
}
|
||||
if seqLen == 1 {
|
||||
return message
|
||||
}
|
||||
@ -108,6 +111,9 @@ func (d *Decoder) Add(data []byte) error {
|
||||
if err := mode.Unmarshal(data, p); err != nil {
|
||||
return fmt.Errorf("fountain: failed to decode fragment: %w", err)
|
||||
}
|
||||
if err := validatePartHeader(p); err != nil {
|
||||
return err
|
||||
}
|
||||
if d.header.SeqLen > 0 {
|
||||
if d.header != p.partHeader {
|
||||
return fmt.Errorf("fountain: incompatible fragment")
|
||||
@ -227,6 +233,9 @@ func Checksum(data []byte) uint32 {
|
||||
|
||||
// SeqNumFor searches for a seqNum that outpus the xor of fragments.
|
||||
func SeqNumFor(seqLen int, checksum uint32, fragments []int) int {
|
||||
if seqLen <= 0 {
|
||||
panic("seqLen out of range")
|
||||
}
|
||||
seqNum := 1
|
||||
sort.Ints(fragments)
|
||||
for {
|
||||
@ -240,7 +249,10 @@ func SeqNumFor(seqLen int, checksum uint32, fragments []int) int {
|
||||
}
|
||||
|
||||
func chooseFragments(seqNum uint32, seqLen int, checksum uint32) []int {
|
||||
if seqNum <= uint32(seqLen) {
|
||||
if seqLen <= 0 {
|
||||
return nil
|
||||
}
|
||||
if uint64(seqNum) <= uint64(seqLen) {
|
||||
return []int{int(seqNum - 1)}
|
||||
} else {
|
||||
seed := binary.BigEndian.AppendUint32(nil, seqNum)
|
||||
@ -258,6 +270,16 @@ func chooseFragments(seqNum uint32, seqLen int, checksum uint32) []int {
|
||||
}
|
||||
}
|
||||
|
||||
func validatePartHeader(p *part) error {
|
||||
if p.SeqLen <= 0 {
|
||||
return fmt.Errorf("fountain: invalid sequence length")
|
||||
}
|
||||
if p.MessageLen < 0 {
|
||||
return fmt.Errorf("fountain: invalid message length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func shuffle(items []int, rng *xoshiro256.Source) []int {
|
||||
var result []int
|
||||
for len(items) > 0 {
|
||||
|
||||
@ -7,8 +7,10 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"seedetcher.com/bc/xoshiro256"
|
||||
)
|
||||
|
||||
@ -161,3 +163,61 @@ func TestChooseFragments(t *testing.T) {
|
||||
t.Errorf("mismatched fragment indexes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderRejectsInvalidHeaders(t *testing.T) {
|
||||
enc, err := cbor.CoreDetEncOptions().EncMode()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
p part
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "seq len zero",
|
||||
p: part{
|
||||
SeqNum: 1,
|
||||
partHeader: partHeader{
|
||||
SeqLen: 0,
|
||||
MessageLen: 1,
|
||||
Checksum: 1,
|
||||
},
|
||||
Data: []byte{0},
|
||||
},
|
||||
want: "invalid sequence length",
|
||||
},
|
||||
{
|
||||
name: "message len negative",
|
||||
p: part{
|
||||
SeqNum: 1,
|
||||
partHeader: partHeader{
|
||||
SeqLen: 1,
|
||||
MessageLen: -1,
|
||||
Checksum: 1,
|
||||
},
|
||||
Data: []byte{0},
|
||||
},
|
||||
want: "invalid message length",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
b, err := enc.Marshal(tc.p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var d Decoder
|
||||
err = d.Add(b)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error containing %q", tc.want)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("got error %q, want substring %q", err.Error(), tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ As for thickness: 1.5mm is the minimum. 2mm is substantial.
|
||||
- [ ] Micro-USB male to USB-A female ([amazon](https://a.co/d/drLFF49))
|
||||
- [ ] Steel Plates, 304/316L, 10x10cm (make sure they are really flat). You can get them on ebay or amazon or cut your own.
|
||||
- [ ] Iron (for ironing clothes)
|
||||
- [ ] 0.5-2mm thick silicone sheet ([amazon](https://a.co/d/2F59LSZ)) for SeedEtcher Transfer Stack, cut 2 pieces of 110x110mm.
|
||||
- [ ] 0.5-2mm thick silicone sheet ([amazon](https://a.co/d/2F59LSZ)) for SeedEtcher Transfer Stack™, cut 2 pieces of 110x110mm.
|
||||
- [ ] Wood board, cork mat (optional)
|
||||
- [ ] Toner Transfer Paper ([amazon](https://a.co/d/dmR4RUL))
|
||||
- [ ] Anti-etching pens ([amazon](https://a.co/d/5DnOhRR)), stop out ground e.g. from [Lascaux](https://lascaux.ch/en/products/brushes-printmaking-sets-various/lascaux-etching?shp3_product=1704) or [Charbonnel](https://intaglioprintmaker.com/product/charbonnel-lamour-black-covering-varnish/) or nail polish
|
||||
@ -116,12 +116,12 @@ The new layout is designed for maximal mask coverage of a 100x100mm plate. Put t
|
||||
|
||||

|
||||
|
||||
*If you sanded to a brushed look, the direction of the brushed lines are important. Light breaks differently on brushed metal depending on the direction they run in. If you hold a plate towards a light source, horizontal lines will diffuse the light and reflections, vertical lines will reflect more. So, the brushed lines should run horizontally to your plate layout. The QR code is easier to scan when looking normally at the plate. This is a detail but it is worth mentioning.*
|
||||
*If you sanded to a brushed look, the direction of the brushed lines is important. Light breaks differently on brushed metal depending on the direction it runs. If you hold a plate towards a light source, horizontal lines will diffuse the light and reflections, vertical lines will reflect more. So, the brushed lines should run horizontally to your plate layout. The QR code is easier to scan when looking normally at the plate. This is a detail but it is worth mentioning.*
|
||||
|
||||
1) ### SeedEtcher Transfer Stack
|
||||
3) ### SeedEtcher Transfer Stack™
|
||||
With the SeedEtcher Transfer Stack it is now possible to heat transfer both sides of the plate at once.
|
||||
|
||||

|
||||

|
||||
|
||||
Pre-heat the iron to around 175°C. (the temperature has to be between 150°C and 180°C but no more than 180°C). Tip: Use a thermometer to figure out how hot your iron gets.
|
||||
Put a paper towel folded 4 times on your wood board (optionally use cork, even better insulator). The board should be on the floor and it should not wiggle.
|
||||
@ -136,14 +136,14 @@ Pressure is important. Do it on the floor where you can really lean onto the iro
|
||||
Optional: Put a stack of steel plates on top of the hot plate (heat sink).
|
||||
Let it cool off completely! It will take a while (20m). The transfer paper should buckle and lift off the metal all by itself. The transfer paper should come off without any toner sticking to it.
|
||||
|
||||
1) ### Bake plate in oven (don't skip this step)
|
||||
5) ### Bake plate in oven (don't skip this step)
|
||||
Bake for 12 minutes at 180°C, no airflow.
|
||||
This reflows the toner and makes it stick even more to the plate and closes pinholes.
|
||||
***IMPORTANT:*** With the new layout most of the plate is covered in toner mask to the edges. Use a baking tray, put two silicon sheets next to each other (they are heat resistant). Place a tee cup that is high enough to lean the plate against on one sheet. The silcone prevents the cup and plate from sliding. Put Bottom edge of the plate on the silicone and the lean the top edege against the cup.
|
||||
Make the setup in cold oven.
|
||||
Let this cool down when done baking and only then move it. If the plate falls while it's hot your toner mask is ruined.
|
||||
|
||||
1) ### Repairs
|
||||
6) ### Repairs
|
||||
If the transfer wasn’t perfect you can do repairs by using nail polish or stop out ground and a small brush or anti-etching pens ([amazon](https://a.co/d/5DnOhRR))
|
||||
|
||||
### Transfer Troubleshooting
|
||||
|
||||
@ -1,60 +1,88 @@
|
||||
# SeedEtcher b0.4 Checklist
|
||||
|
||||
## Goal
|
||||
- Deliver a standalone, cross-platform, offline recovery tool focused on descriptor-share compatibility and inheritance/break-glass workflows without Pi hardware.
|
||||
- Add a direct laser-ablation path (Acmer K1 / GRBL) from SeedEtcher plate data while preserving current print behavior.
|
||||
- Move toward a single source of truth for layout: vector-first `PlateScene`, then backend renderers.
|
||||
|
||||
## Scope
|
||||
- In scope:
|
||||
- Host CLI to recover descriptor payload from legacy `SE1:` shares (migration support).
|
||||
- Host CLI decode support for UR/XOR descriptor-share inputs used in b0.3 release flow.
|
||||
- Cross-platform builds (macOS, Linux, Windows).
|
||||
- Offline-first docs and reproducible release artifacts.
|
||||
- Out of scope:
|
||||
- Webcam/camera scanning.
|
||||
- Network services.
|
||||
- Browser app as canonical recovery path.
|
||||
- Single-plate laser output (`100x100mm`), no paper/cutbox pagination in laser mode.
|
||||
- Direct GRBL streaming in host mode over USB serial.
|
||||
- Vector-first layout model and renderer split.
|
||||
- Maintain visual morphology parity with current etched print style (dot QR + rounded islands + font geometry).
|
||||
- Out of scope (initial b0.4):
|
||||
- LightBurn dependency/workflow.
|
||||
- Multi-plate nesting/auto-arrange in one bed job.
|
||||
- Camera/G-code preview UI.
|
||||
|
||||
## Milestones
|
||||
|
||||
### 1) CLI recovery command
|
||||
- [ ] Add `cmd/recover/main.go`.
|
||||
- [ ] Accept share input via file and stdin.
|
||||
- [ ] Parse and validate `SE1:` shares.
|
||||
- [ ] Reconstruct payload via `descriptor/shard`.
|
||||
- [ ] Output:
|
||||
- [ ] descriptor text
|
||||
- [ ] `UR:CRYPTO-OUTPUT`
|
||||
- [ ] Add clear, deterministic error messages for:
|
||||
- [ ] duplicate share index
|
||||
- [ ] mixed set IDs
|
||||
- [ ] insufficient shares
|
||||
- [ ] malformed share payload
|
||||
### 1) Architecture foundation (single source of truth)
|
||||
- [ ] Define `PlateScene` domain model (mm-based coordinates):
|
||||
- [ ] primitives: path, circle, rounded-rect, text outline, transform/group
|
||||
- [ ] layer tags: `mask`, `guide` (guide disabled by default)
|
||||
- [ ] deterministic origin and bounds (`100x100mm`)
|
||||
- [ ] Define renderer interfaces:
|
||||
- [ ] `SceneRasterRenderer` (for print path parity checks)
|
||||
- [ ] `SceneGCodeRenderer` (for GRBL output)
|
||||
- [ ] optional `SceneSVGRenderer` (debug only)
|
||||
- [ ] Add visual/debug CLI outputs from the same scene:
|
||||
- [ ] `-scene-json-out <file>` (canonical scene dump for diff/tests)
|
||||
- [ ] `-svg-out <dir>` (human visual inspection)
|
||||
- [ ] `-png-out <dir>` from scene raster renderer (quick morphology parity)
|
||||
- [ ] Keep current print pipeline untouched in this phase.
|
||||
|
||||
### 2) Test vectors and verification
|
||||
- [ ] Add roundtrip tests for CLI using known fixture shares.
|
||||
- [ ] Add negative tests (bad/mixed/incomplete share sets).
|
||||
- [ ] Verify CLI output matches controller recovery output for same inputs.
|
||||
### 2) PlateScene builder (layout migration)
|
||||
- [ ] Build scene for seed plate from existing layout rules.
|
||||
- [ ] Build scene for descriptor plate from existing layout rules.
|
||||
- [ ] Preserve current styling semantics:
|
||||
- [ ] QR data modules as circles (dot scale parity)
|
||||
- [ ] registration/finder islands as rounded squares
|
||||
- [ ] exact text anchors/margins/tracking behavior
|
||||
- [ ] Add scene-level geometry snapshot tests for key fixtures.
|
||||
|
||||
### 3) Build and release artifacts
|
||||
- [ ] Add `scripts/build-recover-cli.sh`.
|
||||
- [ ] Build targets:
|
||||
- [ ] `darwin/arm64`
|
||||
- [ ] `darwin/amd64`
|
||||
- [ ] `linux/amd64`
|
||||
- [ ] `linux/arm64`
|
||||
- [ ] `windows/amd64`
|
||||
- [ ] Use `CGO_ENABLED=0` for portable binaries.
|
||||
- [ ] Generate `SHA256SUMS` for all artifacts.
|
||||
### 3) Direct G-code backend (GRBL)
|
||||
- [ ] Add CLI output mode for laser:
|
||||
- [ ] `-gcode-out <dir>`
|
||||
- [ ] `-side seed|desc|both`
|
||||
- [ ] `-plate-mm` (default `100`)
|
||||
- [ ] laser params (`-laser-max-s`, `-laser-feed`, `-rapid-feed`)
|
||||
- [ ] no-send mode by default (generate files first, stream later)
|
||||
- [ ] Emit GRBL-safe preamble/footer:
|
||||
- [ ] `G21`, `G90`, `M4`/`M5`, sane feed defaults
|
||||
- [ ] configurable power scaling `S`
|
||||
- [ ] Implement fill/trace strategy for closed vector regions.
|
||||
- [ ] Add deterministic output tests for small canonical plate fixtures.
|
||||
|
||||
### 4) Documentation
|
||||
- [ ] Add `docs/dev/recover-cli.md`:
|
||||
- [ ] offline usage workflow
|
||||
- [ ] sample commands
|
||||
- [ ] inheritance/break-glass instructions
|
||||
- [ ] compatibility limits (legacy `SE1:` is SeedEtcher-native; UR/XOR follows interoperable wallet share formats)
|
||||
- [ ] Update top-level docs index to link b0.4 checklist and recovery CLI doc.
|
||||
### 4) USB serial transport to K1 (host mode)
|
||||
- [ ] Add GRBL serial transport (`/dev/ttyACM*` or `/dev/ttyUSB*`):
|
||||
- [ ] line-by-line send with `ok`/`error` ack handling
|
||||
- [ ] timeout and retry policy
|
||||
- [ ] alarm/reset handling (`?`, soft reset, unlock flow)
|
||||
- [ ] Ensure transport is isolated from printer `/dev/usb/lp*` path.
|
||||
- [ ] Add a dry-run mode that writes G-code only (no device send).
|
||||
|
||||
### 5) Stretch goals (if time allows)
|
||||
- [ ] Optional output file formats (`.txt`, `.json`).
|
||||
- [ ] Optional strict mode requiring exact threshold count.
|
||||
- [ ] Optional share order normalization output for auditing.
|
||||
### 5) Validation and parity
|
||||
- [ ] Visual parity tests (scene vs current raster) for:
|
||||
- [ ] singlesig
|
||||
- [ ] multisig 2-of-3
|
||||
- [ ] multisig 3-of-5
|
||||
- [ ] Physical validation on K1:
|
||||
- [ ] seed side readability (words + QR)
|
||||
- [ ] descriptor side readability
|
||||
- [ ] scan success in Sparrow/Seed tools
|
||||
- [ ] Regression check: existing PCL/PS/HBP print flows unchanged.
|
||||
|
||||
### 6) Documentation and release
|
||||
- [ ] Add `docs/dev/laser-grbl.md`:
|
||||
- [ ] machine setup
|
||||
- [ ] CLI examples
|
||||
- [ ] safe power/feed starting profiles
|
||||
- [ ] troubleshooting (`error`, `alarm`, mis-scale, offsets)
|
||||
- [ ] Update `docs/development.md` with laser output flags.
|
||||
- [ ] Add changelog entry when laser path is user-ready.
|
||||
|
||||
## Stretch goals
|
||||
- [ ] Real-time progress reporting for GRBL send in UI.
|
||||
- [ ] Optional SVG export from `PlateScene` for inspection/debug.
|
||||
- [ ] Optional path optimization pass (travel reduction).
|
||||
|
||||
Loading…
Reference in New Issue
Block a user