mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-28 01:01:12 -07:00
Retire BRINGUP.md and CAMPAIGN-LOG.md
Every fact in the two documents is now on the documentation site, which is the single source of truth. This repository carries no project documentation any more: it is the build and release base plus the acceptance tool, the bench tools and the fixture firmware. BRINGUP.md was the runbook, the hardware facts bank and the open-work list. CAMPAIGN-LOG.md was the dated record of how each result was obtained. What replaces them: the site for present state, and the commit message for the record of what a change did and how it was proven, so the change and its record stay together. Local open work is the developer's own file at the tree root and is not tracked here. README.md becomes an index card: what this is, build, test, and where the documentation is. The release pipeline tags the documentation. Firmware on a machine needs the documentation that agrees with it, so release.sh now tags the forgefirm-docs checkout with the same v<version> as the release, and prints the command that pushes the tag with the release. The checkout must exist and be clean, which is a new gate before the signature. FORGEFIRM_DOCS_DIR names the checkout (default: the sibling one) and FORGEFIRM_DOCS_SKIP releases without a tag, loudly, and is never the default. The tag is made at staging and pushed with the release, never before: a documentation tag for a release that never shipped is worse than no tag. No catalog consequence. release.sh is host-side and is in no image. The commission.py change is one sentence of a test description, not behavior. accel_crash_probe.py and the kas header lose pointers to the retired files. Checks: bash -n and sh -n on release.sh, and the tracked trees carry no reference to either retired file.
This commit is contained in:
@@ -0,0 +1,391 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Instructions for AI coding agents that work in this repository. A human
|
||||||
|
contributor is welcome to read it too: it is the same set of rules.
|
||||||
|
|
||||||
|
## ForgeFIRM repository rules
|
||||||
|
|
||||||
|
### Safety on the machine
|
||||||
|
|
||||||
|
- The laser never fires with the coolant pump deliberately off. The heater-only
|
||||||
|
flow-calibrate and flow-verify tools are the only pump-off state, and the
|
||||||
|
tube stays dark during them.
|
||||||
|
- The 40 V motor rail stays up while the machine is on. Never cycle it for a
|
||||||
|
controller handover. A rail glitch can leave the stepper drivers
|
||||||
|
unserviceable.
|
||||||
|
|
||||||
|
### The iteration loop
|
||||||
|
|
||||||
|
- Edit, cross-build in the Yocto build environment, hot-deploy to the
|
||||||
|
machine, prove. Then one commit, one push, one pin bump for the proven
|
||||||
|
change.
|
||||||
|
- A full image build happens once, at the end of a work item, never per fix.
|
||||||
|
- Kernel and BSP changes ride one image flash, batched. Validate a `.ko` or
|
||||||
|
overlay change on the image that ships it. Never hot-swap a `.ko` onto a
|
||||||
|
board that is about to be reflashed.
|
||||||
|
|
||||||
|
### Acceptance coverage
|
||||||
|
|
||||||
|
- Evaluate every change against the acceptance catalog in
|
||||||
|
`forgetest/forgetest/suite/`. Does an existing test exercise the changed
|
||||||
|
behavior? Does its `covers` map name the files you touched? If not, add or
|
||||||
|
widen one in the same change. The coverage lint
|
||||||
|
(`python3 -m forgetest.coverage --enforce`) is the floor.
|
||||||
|
- The `SRCREV` of a component and the `PV` that moves with it live in
|
||||||
|
`<recipe>-pin.inc` next to the recipe. Nothing else goes in that file. The
|
||||||
|
image manifest keeps `*-pin.inc` out of the layer content hash. Thus a pin
|
||||||
|
bump invalidates only the acceptance tests that cover that component. A
|
||||||
|
pin in a recipe body counts as a platform change and needs a full
|
||||||
|
acceptance campaign.
|
||||||
|
- Test a harness change yourself, on the host, before you hand it to the
|
||||||
|
operator for a bench run. A harness that fails on its own defect costs the
|
||||||
|
operator a full re-run cycle.
|
||||||
|
- A test declares its `mode`, and the runner switches. Cloud tests stay in
|
||||||
|
cloud mode. Keep `requires` lists minimal, and never gate one test behind a
|
||||||
|
chain of others.
|
||||||
|
- Never exercise a gate or a limit through the `GFCOOL_*` environment
|
||||||
|
overrides in a test. Use the settings API, and restore it in the teardown.
|
||||||
|
|
||||||
|
### CI couplings that set the push order
|
||||||
|
|
||||||
|
- The CI of `grblHAL-glowforge` gets the laser harnesses from `scripts/bench/`
|
||||||
|
at the head of `master`, unpinned. Push a harness change here before the
|
||||||
|
driver change that needs it.
|
||||||
|
- The acceptance page shares its theme and its vendored Bootstrap with the
|
||||||
|
`forgectrl` panel, byte for byte. Land a UI change in `forgectrl` first.
|
||||||
|
Push it and pin it. Only then push this repository.
|
||||||
|
- The CI of `kernel-module-glowforge` cross-builds against the BSP from
|
||||||
|
`meta-openglow`. Push `meta-openglow` before a module change that needs a
|
||||||
|
BSP change.
|
||||||
|
|
||||||
|
### Builds and images
|
||||||
|
|
||||||
|
- Builds run only in the dedicated Yocto build environment that the Build page
|
||||||
|
of the site defines (kas and bitbake). Never use a native host build of a
|
||||||
|
component as proof, not even for a compile check.
|
||||||
|
- Every build makes both images: `forgefirm-image` (the release image) and
|
||||||
|
`forgefirm-image-dev` (the bench image, with `forgetest` on port 8090). The
|
||||||
|
dev image needs an explicit `--target`. Report both artifact names.
|
||||||
|
- After a kernel-module pin bump, rebuild the kernel and the module in the
|
||||||
|
same run: `bitbake -c cleansstate linux-fslc kernel-module-glowforge`, then
|
||||||
|
the images. The kernel version suffix is not reproducible across
|
||||||
|
re-checkouts, and a rootfs whose kernel and module disagree fails.
|
||||||
|
- Read the layer HEAD lines at the top of a build log before you trust a build
|
||||||
|
that moves a pin.
|
||||||
|
|
||||||
|
### The record of the work
|
||||||
|
|
||||||
|
This repository carries no status document and no dated log. Both were
|
||||||
|
retired: every fact about the machine and the firmware is on the
|
||||||
|
documentation site, and **the record of a piece of work is the commit message
|
||||||
|
that carries it**: what was done, how it was proven, and what it replaced, in
|
||||||
|
the same place as the change itself.
|
||||||
|
|
||||||
|
A measurement session or a bench drill is written up in the commit that
|
||||||
|
lands its result. Nothing accumulates in a file.
|
||||||
|
|
||||||
|
### On the bench
|
||||||
|
|
||||||
|
These rules apply whenever an agent works with the operator at a machine.
|
||||||
|
|
||||||
|
- **One armed run per turn.** Start the run, let it finish, stop, report what
|
||||||
|
happened against what was expected, and wait for the operator's
|
||||||
|
confirmation before any further armed run or machine command. Never chain a
|
||||||
|
jog, a fix, or a re-run onto a burn in the same step. Dry runs (latch
|
||||||
|
locked, no emission) can be batched. Say which is which before you start.
|
||||||
|
- **Zero inference during live fire.** Report only what the combined
|
||||||
|
observations confirm: the operator's eyes, the drill output, the logs, the
|
||||||
|
sysfs readbacks. Everything else is labeled "unconfirmed" and is not acted
|
||||||
|
on.
|
||||||
|
- **One Grbl connection.** Never open a second connection to TCP port 23 while
|
||||||
|
a drill, an acceptance test, or a sender holds one. The last connection
|
||||||
|
wins, the displaced client's cleanup never arrives, and its queued commands
|
||||||
|
wait behind the arm gate. Read state through `forgectrl` (HTTP) and sysfs
|
||||||
|
only. After a displaced or aborted drill: soft reset, confirm that the RX
|
||||||
|
buffer reads full, `M5`, confirm that `armed` is false, before anyone
|
||||||
|
touches the button.
|
||||||
|
- **The operator touches the machine.** The operator runs the install, the
|
||||||
|
service restart, and the reflash on the machine. Never modify the software
|
||||||
|
of a running machine unless the operator asks for that exact step.
|
||||||
|
- **Bench hygiene.** Stage test files in `/tmp`. A file that must survive a
|
||||||
|
reboot goes in `/data/bench-scratch/`, and that directory is deleted whole
|
||||||
|
at the end of the session. Nothing goes loose in `/data`. Remove everything
|
||||||
|
you put on the board, in the same session, and list `/data` before you end.
|
||||||
|
A bench tool worth a second use goes in `scripts/bench/`. The dev image
|
||||||
|
installs those tools under `/usr/share/forgetest/bench/`. Run them from
|
||||||
|
there.
|
||||||
|
|
||||||
|
## Project-wide rules
|
||||||
|
|
||||||
|
The sections above are specific to this repository. The rules below apply to
|
||||||
|
every repository of the OpenGlow ForgeFIRM project: the firmware components,
|
||||||
|
the build base, the documentation, and the hardware designs. Where a
|
||||||
|
repository rule and a rule below conflict, the safety, proof, and hygiene
|
||||||
|
rules below win. The documentation site, <https://docs.forgefirm.org/>, is
|
||||||
|
the source of truth for every fact about the machine and the firmware. Read
|
||||||
|
its Developers section before you change code.
|
||||||
|
|
||||||
|
### What ForgeFIRM is
|
||||||
|
|
||||||
|
ForgeFIRM is open firmware for stock Glowforge lasers (Basic, Plus, Pro). It
|
||||||
|
runs on the factory NXP i.MX6 control board with no hardware modification. It
|
||||||
|
replaces the cloud-dependent factory userspace with an open Linux image that
|
||||||
|
controls the machine locally. In GRBL mode the machine is a standard grblHAL
|
||||||
|
controller for LightBurn and other senders on TCP port 23. Cloud mode runs the
|
||||||
|
factory experience through the Glowforge web service, and it is a deliberate,
|
||||||
|
maintained feature. The stack is hardware validated: GRBL mode cuts real jobs,
|
||||||
|
and cloud mode runs end to end.
|
||||||
|
|
||||||
|
One controller runs at a time under `forgectrl`, the machine-services daemon,
|
||||||
|
which owns the pulse device, the cooling engine, the cameras, and the control
|
||||||
|
panel. The kernel module plays the pulse stream into the stepper drivers and
|
||||||
|
owns the laser latch and the safety readbacks. The Technical section of the
|
||||||
|
site describes the whole stack.
|
||||||
|
|
||||||
|
### The repositories
|
||||||
|
|
||||||
|
All repositories live under the OpenGlow organization on GitHub,
|
||||||
|
<https://github.com/openglow-org>. For development, check every repository out
|
||||||
|
as a sibling in one base directory named `openglow-forgefirm`. The build
|
||||||
|
scripts expect that layout. Each repository is its own git repository. The
|
||||||
|
base directory is not.
|
||||||
|
|
||||||
|
| Repository | Role | Default branch | License |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `forgefirm` | The build and release base: the `meta-forgefirm` Yocto layer, the kas configuration, the image recipes, the install and release scripts, the acceptance tool `forgetest/`, the bench tools `scripts/bench/`, and the bench actuator firmware `fixture/`. It carries no project documentation. | `master` | MIT (layer metadata) |
|
||||||
|
| `meta-openglow` | The BSP layers `meta-glowforge-bsp` and `meta-openglow-core`: kernel, device tree, U-Boot, board recipes. | `scarthgap` | MIT (layer metadata) |
|
||||||
|
| `forgectrl` | The machine-services daemon (C) on HTTPS port 443, with the read-only routes on HTTP port 80: supervisor, pulse-device broker, motion-liveness gate, cooling engine, cameras, telemetry, settings, diagnostics, control panel, A/B updates. | `main` | MIT |
|
||||||
|
| `grblHAL-glowforge` | The grblHAL driver for the stock board, the GRBL-mode controller. The core is a submodule at `src/grbl`. Machine constants are in `src/boards/glowforge.h`. | `main` | GPL-3.0-or-later |
|
||||||
|
| `grblHAL-core` | The fork of the grblHAL core that the driver uses. Minimum change, no rationale comments. | `forgefirm` | The upstream grblHAL license |
|
||||||
|
| `kernel-module-glowforge` | `glowforge.ko`: the SDMA + EPIT pulse engine, the laser latch, the safety readbacks, the sensors. | `master` | GPL-2.0-or-later |
|
||||||
|
| `python3-gfhardware` | The `gfhardware` hardware library and the cloud-mode applications in `forgefirm-app/`. | `master` | MIT, with one LGPL-2.1-or-later component |
|
||||||
|
| `Glowforge-Utilities` | `gfutilities` on PyPI: the factory protocol and service layer that cloud mode uses. | `master` | MIT |
|
||||||
|
| `forgefirm-docs` | The documentation site. | `main` | CC BY-SA 4.0 |
|
||||||
|
| `openglow-serial-adapter` | The USB-C serial-console adapter for the control board. | `main` | CC BY-NC-SA 4.0 |
|
||||||
|
|
||||||
|
The full table, the license details, and the component diagram are on the
|
||||||
|
site: <https://docs.forgefirm.org/developers/>.
|
||||||
|
|
||||||
|
### Safety first, and in that order
|
||||||
|
|
||||||
|
The machine is a laser, and the operator is the safety authority at it. These
|
||||||
|
rules bind every change, in every repository, that can affect what the machine
|
||||||
|
does.
|
||||||
|
|
||||||
|
- The hardware chain is the safety boundary. Software only adds gates on top
|
||||||
|
of it. `LASER_ON` is never a bare GPIO. The kernel laser latch is locked by
|
||||||
|
default, and every close of the pulse device locks it again.
|
||||||
|
- Never defeat a switch, an interlock, or a readback, in hardware or in
|
||||||
|
software, not even for a test.
|
||||||
|
- Never change a fire gate, a thermal limit, or a safety default to make a
|
||||||
|
test pass.
|
||||||
|
- The order of work is absolute: emission and motion first, robustness and
|
||||||
|
hygiene after. Review a change that touches emission or motion for that
|
||||||
|
before anything else.
|
||||||
|
- A change that can put energy where it was not commanded gets a regression
|
||||||
|
test with the fix, in the same commit, never after.
|
||||||
|
- Position counters, homing anchors, and a homed flag are not proof of
|
||||||
|
physical motion. The head accelerometer is, and so are the operator's eyes.
|
||||||
|
|
||||||
|
### Proof before done
|
||||||
|
|
||||||
|
A feature or fix is not complete until it is proven. The order of preference:
|
||||||
|
|
||||||
|
1. A host test that runs in CI. The Test page of the site lists the host tests
|
||||||
|
of each repository.
|
||||||
|
2. A bench drill on the bench reference, recorded in the commit message that
|
||||||
|
carries the change.
|
||||||
|
3. Documented reasoning.
|
||||||
|
|
||||||
|
Also:
|
||||||
|
|
||||||
|
- Run the host tests of this repository before every commit. Where the build
|
||||||
|
uses `-Werror`, a warning is a failure.
|
||||||
|
- A component that ships in the image is also evaluated against the release
|
||||||
|
acceptance catalog in the `forgefirm` repository (Test page, "Coverage
|
||||||
|
currency"). A behavior change with no catalog consequence gets a sentence
|
||||||
|
of justification in the commit message.
|
||||||
|
- For a component that ships in the image, the proof build is the Yocto
|
||||||
|
cross-build that the Build page of the site defines, never a native host
|
||||||
|
build.
|
||||||
|
- Report outcomes faithfully. Quote failing output as it is. Name a skipped
|
||||||
|
step as skipped. Never narrate an expected result as an observed one.
|
||||||
|
|
||||||
|
### The workflow: prove locally, push when proven
|
||||||
|
|
||||||
|
- Nothing goes to a public repository until it is proven: in the host tests
|
||||||
|
for host-only changes, on the bench for changes that alter what the machine
|
||||||
|
does.
|
||||||
|
- Iterate locally. Then make one commit and one push for the proven change.
|
||||||
|
No chains of fix-up pushes. Every push runs CI, and CI minutes are a budget.
|
||||||
|
- Commit only finished work, and only when asked. Never commit intermediate
|
||||||
|
findings, working notes, plans, audits, or status files. If a conclusion is
|
||||||
|
still moving, it is not ready for a repository. Unpushed mistakes come out
|
||||||
|
with `git reset --mixed <base>`, which keeps the working tree.
|
||||||
|
- "Commit" and "commit and build" include the push of every touched
|
||||||
|
repository, in dependency order. Never hold a push back to protect earlier
|
||||||
|
unpushed commits on the same branch. The only exception is an explicit "do
|
||||||
|
not push" for that session.
|
||||||
|
- Never push, force-push, tag, cut a release, refresh a lockfile, bump a
|
||||||
|
submodule pointer, or bump a pin unless asked.
|
||||||
|
|
||||||
|
### Pins
|
||||||
|
|
||||||
|
A firmware component reaches the image only through its pin: an exact
|
||||||
|
`SRCREV` in `meta-forgefirm` (in the `forgefirm` repository) for ForgeFIRM
|
||||||
|
components, or in `meta-openglow` for BSP components. There is no `AUTOREV`.
|
||||||
|
A commit in a component repository changes nothing on a machine until its pin
|
||||||
|
moves. The order is always: push the source repository, bump its pin, then
|
||||||
|
run `bitbake -c fetch <recipe>` to make sure that the pin resolves. Some
|
||||||
|
pairs of repositories have a CI coupling that fixes the push order between
|
||||||
|
them. The Release flow page of the site lists them, and each affected
|
||||||
|
repository names its own in the section above.
|
||||||
|
|
||||||
|
### Git conventions
|
||||||
|
|
||||||
|
- A commit is attributed to the human who is responsible for it, in both the
|
||||||
|
author and the committer fields. Never list an AI assistant as an author or
|
||||||
|
a co-author. Never add an AI attribution trailer or a "Generated with" line
|
||||||
|
to a commit message or a pull request. In a fresh clone, set the local
|
||||||
|
`user.name` and `user.email` before the first commit.
|
||||||
|
- A commit message says what changed and why, in the present tense, in
|
||||||
|
Simplified Technical English. The "why" of a change to fork code lives here
|
||||||
|
and nowhere else.
|
||||||
|
- Keep line endings LF in every file that Linux tooling consumes: patches,
|
||||||
|
shell scripts, recipes, device trees. Check the bytes after an edit from a
|
||||||
|
Windows tool. A CRLF patch breaks `do_patch`.
|
||||||
|
- Never commit build output, `__pycache__`, virtual environments, editor
|
||||||
|
files, a local knowledge graph (`graphify-out/`), captured machine data that
|
||||||
|
carries an identity, tokens, keys, or passwords.
|
||||||
|
- Never rewrite published history unless the operator asks for it.
|
||||||
|
- A pull request to an upstream repository is never automatic. The developer
|
||||||
|
decides if and when to send one. You can remind them that upstream
|
||||||
|
candidates exist, but the decision is theirs.
|
||||||
|
|
||||||
|
### Writing rules
|
||||||
|
|
||||||
|
They apply to everything: documentation, README files, code comments,
|
||||||
|
docstrings, log and console text, commit messages, issue text, and replies.
|
||||||
|
|
||||||
|
- **American English.** analyze, behavior, color, center, gray, catalog,
|
||||||
|
license, judgment, program, artifact, percent, toward, among, while,
|
||||||
|
learned. The style lint in `forgefirm-docs` rejects the British forms.
|
||||||
|
- **ASD-STE100 Simplified Technical English.** Short sentences: 20 words in
|
||||||
|
a procedure, 25 in a description. One topic per sentence, one instruction
|
||||||
|
per sentence. Active voice, imperative in procedures, simple present in
|
||||||
|
descriptions. "Must" for necessity, "can" for possibility. No "should",
|
||||||
|
"would", "could", "may". Prefer: do, make sure, use, start, show, occur,
|
||||||
|
before, after, because, for example, that is. No "etc.", no "and/or".
|
||||||
|
Noun clusters of three words at most. Warnings and cautions come before the
|
||||||
|
step they protect. Warm and direct is good. A joke that shades a fact is
|
||||||
|
not.
|
||||||
|
- **No em dashes.** Never, anywhere. Use a colon, a period, or a comma.
|
||||||
|
- **Present tense, present state.** Text describes the code and the machine
|
||||||
|
as they are. No history narrative, no "the old version did", no "evolved
|
||||||
|
from", no story of how the code got here. Upstream copyright and
|
||||||
|
derivation attribution to public projects stays: that is license credit,
|
||||||
|
not narrative.
|
||||||
|
- **No workstation paths.** Nothing outside a repository appears as a path in
|
||||||
|
it: no drive letters, no WSL mounts, no home directories, no sibling-repo
|
||||||
|
relative paths, no private reference directories. Name the artifact
|
||||||
|
instead: "the captured factory pulse files", "that bench session".
|
||||||
|
- **No bench-machine identity.** Never a hostname, a serial number, a fuse
|
||||||
|
value, a private IP address, or a login of the bench machine, in any file,
|
||||||
|
comment, commit message, sample, or test fixture. Use neutral placeholders
|
||||||
|
of the `ABC-123` form. Record the fact of a verification, never the values.
|
||||||
|
- **No factory firmware code.** Never cite the factory firmware binary or its
|
||||||
|
code: no offsets, no addresses, no quoted or decompiled code. State the
|
||||||
|
recovered fact (a formula, a constant, a behavior) and stop. Hardware
|
||||||
|
addresses (IOMUX pad values, sensor registers, boot offsets) are fine.
|
||||||
|
- **No rationale comments in fork code.** Code destined upstream (for
|
||||||
|
example, the grblHAL core fork) carries the minimum change and no "why we
|
||||||
|
did this" comments. The reasoning goes in the commit message. Patches
|
||||||
|
carried in the Yocto layers are exempt. The `ForgeFIRM:` comment convention
|
||||||
|
there is deliberate.
|
||||||
|
- **Interface docs describe the lever, not the policy.** A sysfs attribute
|
||||||
|
doc says what the attribute is, its range, its units, its conversion. How
|
||||||
|
the factory firmware drives it does not belong there.
|
||||||
|
- **No definitive legal or regulatory statements.** The project is not a
|
||||||
|
lawyer. The only permitted form: modifying the machine, including
|
||||||
|
replacing its firmware, may have legal and regulatory ramifications, and it
|
||||||
|
is the end user's responsibility to adhere to the laws, regulations,
|
||||||
|
certifications, and insurance terms that apply where they are. Warranty
|
||||||
|
wording is "may void your warranty", never "voids". Never cite a regulation,
|
||||||
|
a standard, or a certification regime as applying or not applying. The
|
||||||
|
affiliation disclaimer lives in the site footer only. It is also the user's
|
||||||
|
responsibility to make sure that they stay within the vendor's terms of
|
||||||
|
service when they use a feature that calls that vendor's cloud services.
|
||||||
|
- **SPDX.** Every new source file carries an `SPDX-License-Identifier` line
|
||||||
|
under the license of its repository.
|
||||||
|
|
||||||
|
### Where documentation goes
|
||||||
|
|
||||||
|
- **One home.** A fact lives on the documentation site, or it does not exist.
|
||||||
|
A repository README says what the repository is, how to build and test it,
|
||||||
|
and where the documentation is. Nothing else.
|
||||||
|
- **The currency rule.** A change carries a documentation commit when it adds,
|
||||||
|
removes, or renames an interface (a sysfs attribute, an HTTP route, a
|
||||||
|
settings key, a G-code or `$` setting), or when it corrects a measured
|
||||||
|
fact. A measured fact says how it was obtained, and on what: unless the page
|
||||||
|
says otherwise, a measurement on the site was taken on the bench reference,
|
||||||
|
which the site defines.
|
||||||
|
- **A moved document is deleted.** No stub and no redirect stays at the old
|
||||||
|
path.
|
||||||
|
- **Every diagram is Mermaid.** No ASCII art and no image of a diagram.
|
||||||
|
- There is no roadmap page. Open items are tracked as GitHub issues once the
|
||||||
|
repositories accept them.
|
||||||
|
- Do not add plan files, audit files, working notes, or status files to any
|
||||||
|
repository. The project has **no status document and no dated log**: the
|
||||||
|
site describes the present, and the record of what was done, how it was
|
||||||
|
proven, and what it replaced goes in the commit message that carries the
|
||||||
|
change.
|
||||||
|
|
||||||
|
### Analysis and reporting
|
||||||
|
|
||||||
|
- Rank engineering options on technical merit only: performance, correctness,
|
||||||
|
robustness, verifiability. Process cost (an acceptance campaign, an image
|
||||||
|
flash, a new dependency) is a one-line footnote, never a ranking factor.
|
||||||
|
The operator decides the process cost.
|
||||||
|
- Never infer or assume a hardware fact. Label every unverified claim as
|
||||||
|
unverified, and say how to verify it.
|
||||||
|
- Report only what the observations confirm. Everything else is labeled
|
||||||
|
"unconfirmed" and is not acted on.
|
||||||
|
|
||||||
|
### General practice for agents
|
||||||
|
|
||||||
|
- Read the README of this repository, this file, and the Developers section
|
||||||
|
of the site before you change code.
|
||||||
|
- Orient in the code before you edit. If a local knowledge graph
|
||||||
|
(`graphify-out/`) exists in the working tree or its parent, query it first.
|
||||||
|
It is a local artifact and is never committed.
|
||||||
|
- Keep a change minimal and scoped to the request. No drive-by reformatting,
|
||||||
|
no unrelated cleanups, no renames for taste. Match the style of the
|
||||||
|
surrounding code.
|
||||||
|
- Do not add a dependency without a stated reason. The target is a
|
||||||
|
single-core machine with a small eMMC, and every package rides the image.
|
||||||
|
- Extend an existing test or tool instead of adding a parallel one.
|
||||||
|
- Never create a file in the repository that the request does not need.
|
||||||
|
- Ask before anything destructive or outward-facing: a push, a force-push, a
|
||||||
|
tag, a release, a history rewrite, or any change to a machine.
|
||||||
|
- When a fact is unknown, say so and say how to find out. Never fill a gap
|
||||||
|
with a plausible number.
|
||||||
|
- Use the exact names of the project: kas, bitbake, pin, SRCREV, campaign,
|
||||||
|
fingerprint, drill, sysfs, the latch, the armed window. Do not invent names.
|
||||||
|
|
||||||
|
### Before you commit
|
||||||
|
|
||||||
|
1. The host tests of this repository pass, with `-Werror` where the build
|
||||||
|
uses it.
|
||||||
|
2. The change is proven at the highest level available: CI test, bench drill,
|
||||||
|
or documented reasoning.
|
||||||
|
3. For a component that ships in the image: the acceptance catalog covers the
|
||||||
|
changed behavior, or the commit message says why it has no catalog
|
||||||
|
consequence.
|
||||||
|
4. New files carry an SPDX line. Line endings are LF.
|
||||||
|
5. No em dashes, no British spellings, no workstation paths, no bench
|
||||||
|
identity, no factory firmware code, no history narrative, no AI
|
||||||
|
attribution.
|
||||||
|
6. Documentation is current: interfaces and measured facts (the currency
|
||||||
|
rule).
|
||||||
|
7. The commit is one settled change, attributed to the human author, and it
|
||||||
|
is pushed only when the operator asks.
|
||||||
@@ -1,87 +1,103 @@
|
|||||||
# OpenGlow/ForgeFIRM Firmware for Glowforge
|
# OpenGlow / ForgeFIRM firmware for Glowforge
|
||||||
|
|
||||||
> # ⚠️ BETA
|
> ### BETA
|
||||||
>
|
>
|
||||||
> **ForgeFIRM is in beta.** Every release below 0.1.0 is a beta release.
|
> **ForgeFIRM is in beta.** Every release below 0.1.0 is a beta release.
|
||||||
> Expect problems, and expect frequent updates. Upgrade whenever a newer
|
> Expect problems, and expect frequent updates. Upgrade whenever a newer
|
||||||
> release is available, and report what you find on the
|
> release is available, and report what you find on the
|
||||||
> [community forum](https://community.openglow.org).
|
> [community forum](https://community.openglow.org).
|
||||||
|
|
||||||
Open-source firmware for Glowforge brand CNC lasers. ForgeFIRM replaces the
|
Open firmware for Glowforge brand CNC lasers. ForgeFIRM replaces the
|
||||||
cloud-dependent factory software on the **stock control board** - no hardware
|
cloud-dependent factory software on the **stock control board**, with no
|
||||||
modification - and gives the machine a local controller, a local web control
|
hardware modification, and gives the machine a local controller, a local web
|
||||||
panel, and a standard Grbl interface.
|
control panel, and a standard Grbl interface. The factory cloud experience
|
||||||
|
stays available as an option.
|
||||||
|
|
||||||
* [Latest Release](https://github.com/openglow-org/forgefirm/releases)
|
This repository is the **base of the build and of the release**: the
|
||||||
* [Installation Instructions](https://docs.forgefirm.org/install/)
|
`meta-forgefirm` Yocto layer, the kas configuration, the image recipes, the
|
||||||
* [Build Instructions](https://docs.forgefirm.org/developers/building/)
|
install and release scripts, the acceptance tool (`forgetest/`), the bench
|
||||||
* [Connecting LightBurn](https://docs.forgefirm.org/usage/lightburn/)
|
tools (`scripts/bench/`), the bench actuator firmware (`fixture/`), and the
|
||||||
* [How motion and the laser are driven](https://docs.forgefirm.org/technical/forgefirm/)
|
release artifacts (`releases/`).
|
||||||
* [How cooling and airflow work](https://docs.forgefirm.org/technical/forgefirm/cooling-engine/)
|
|
||||||
* [The cameras and the video stream](https://docs.forgefirm.org/usage/cameras/)
|
## Start here
|
||||||
* [How the laser safing works](https://docs.forgefirm.org/safety/)
|
|
||||||
* [How a release is tested before being accepted](https://docs.forgefirm.org/developers/acceptance/)
|
**<https://docs.forgefirm.org/>** is the documentation, and the source of
|
||||||
* [Community Support](https://community.openglow.org)
|
truth for every fact about the machine and the firmware.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| Read this first | [Safety](https://docs.forgefirm.org/safety/) |
|
||||||
|
| Put it on a machine | [Installation](https://docs.forgefirm.org/install/) |
|
||||||
|
| Use it | [Usage](https://docs.forgefirm.org/usage/), [LightBurn](https://docs.forgefirm.org/usage/lightburn/) |
|
||||||
|
| How the machine works | [Technical](https://docs.forgefirm.org/technical/machine/) |
|
||||||
|
| How ForgeFIRM works with it | [ForgeFIRM internals](https://docs.forgefirm.org/technical/forgefirm/) |
|
||||||
|
| Build, test, release | [Developers](https://docs.forgefirm.org/developers/) |
|
||||||
|
| Downloads | [Releases](https://github.com/openglow-org/forgefirm/releases) |
|
||||||
|
| Questions | [Community forum](https://community.openglow.org) |
|
||||||
|
|
||||||
## What it does
|
## What it does
|
||||||
|
|
||||||
**Two controller modes, selected in the web panel and switchable while the
|
Two controller modes, selected in the web panel and switchable while the
|
||||||
machine is idle:**
|
machine is idle. **GRBL mode** runs grblHAL on the machine, speaking Grbl 1.1
|
||||||
|
over TCP port 23, so LightBurn, UGS and cncjs drive the laser directly; motion
|
||||||
|
runs on the board's own hardware step engine, fed live by a local planner.
|
||||||
|
**Cloud mode** signs in to the Glowforge web service as itself, so the phone
|
||||||
|
and web apps work as they always did; it is optional and off by default.
|
||||||
|
Around both sits a local web control panel: status and position, coolant and
|
||||||
|
fan telemetry, safety-switch states, a live camera stream, settings, hardware
|
||||||
|
diagnostics, firmware updates and boot-slot management.
|
||||||
|
|
||||||
* **GRBL mode**: [grblHAL](https://github.com/grblHAL) runs on the machine and
|
The control board is common to the Basic, the Plus and the Pro, and one image
|
||||||
speaks Grbl 1.1 over TCP port 23, so LightBurn, UGS, cncjs, etc... drive the
|
covers every model. The 5 MP camera is hardware validated; the 8 MP camera of
|
||||||
laser directly. Motion runs on the board's own hardware step engine (SDMA +
|
an "HD" machine has a complete path that has never run on one
|
||||||
EPIT), fed live by a local planner. M3/M4 dynamic laser power, coolant-flow
|
([Cameras](https://docs.forgefirm.org/technical/machine/cameras/)).
|
||||||
verification, over-temp holds, and an operator button press to arm the laser
|
|
||||||
for each job.
|
|
||||||
* **Cloud mode**: The machine signs in to the Glowforge web service with its
|
|
||||||
own identity, names its software as ForgeFIRM, and uses the service the way
|
|
||||||
a stock machine does, so the phone and web apps work as they always did.
|
|
||||||
Optional, and off by default. GRBL mode jogs and cuts without it; the one
|
|
||||||
GRBL-mode function that still reaches the Glowforge service is
|
|
||||||
camera-referenced homing (below), until limit-switch homing lands.
|
|
||||||
* A **web control panel**: Machine status and position, coolant
|
|
||||||
and fan telemetry, safety-switch states, live camera stream, machine settings, hardware diagnostics, firmware updates, and boot-slot management.
|
|
||||||
* **Camera-referenced homing**: `$H` from any sender runs the factory-style
|
|
||||||
camera homing cycle through the Glowforge service (a Glowforge account and a
|
|
||||||
live service session are required for `$H`; everything else in GRBL mode
|
|
||||||
runs without them).
|
|
||||||
|
|
||||||
## Hardware
|
## Build
|
||||||
|
|
||||||
The control board is common to Glowforge Basic, Plus, and Pro. The 5 MP
|
```sh
|
||||||
(OV5648) camera modules are fully supported and hardware-validated. The 8 MP
|
kas build kas/forgefirm-glowforge.yml
|
||||||
(OV8856) modules found in "HD" units have a complete capture path - the kernel
|
```
|
||||||
patches, device tree and sensor-aware capture profile they need are all in the
|
|
||||||
build, but it has never run on an 8 MP machine, so treat it as untested.
|
[Build](https://docs.forgefirm.org/developers/building/) covers the host
|
||||||
|
setup, the two images, the source variant and the debug kernel.
|
||||||
|
[Release flow](https://docs.forgefirm.org/developers/release-flow/) covers the
|
||||||
|
pins, the push order and the signing pipeline.
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd forgetest && python3 -m unittest discover -s tests -v
|
||||||
|
```
|
||||||
|
|
||||||
|
The acceptance catalog that gates a release, and the bench tools, are on
|
||||||
|
[Acceptance](https://docs.forgefirm.org/developers/acceptance/) and
|
||||||
|
[The bench](https://docs.forgefirm.org/developers/bench/).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
[AGENTS.md](AGENTS.md) carries the rules for this repository and for the
|
||||||
|
project: safety ordering, proof before done, the push order, and the writing
|
||||||
|
rules. They apply to human contributors too, and
|
||||||
|
[Contribute](https://docs.forgefirm.org/developers/contributing/) is the same
|
||||||
|
set on the site.
|
||||||
|
|
||||||
## What this costs
|
## What this costs
|
||||||
|
|
||||||
ForgeFIRM is free in both senses: free as in beer, free as in speech. All of
|
Nothing. ForgeFIRM is free in both senses, under MIT and GPL licenses. There
|
||||||
it is public and released under MIT and GPL licenses. Read it, build it,
|
is no paid tier, no license key, no subscription and no Pro edition. If
|
||||||
change it, use it, pass it on.
|
someone offers to sell it to you, the licenses allow it, but what you take
|
||||||
|
home is their build rather than this one: get it from the source.
|
||||||
The work happens in public, including the parts that don't work yet, which
|
|
||||||
are written up with more candor than flatters anyone.
|
|
||||||
No paid tier. No license key, no subscription, no activation, no Pro edition,
|
|
||||||
no feature parked behind a paywall. Nothing is held back for a rainy day,
|
|
||||||
mostly because there's no plan for a rainy day.
|
|
||||||
|
|
||||||
If someone offers to sell you this firmware, the licenses allow it and
|
|
||||||
nobody's calling it theft. Just note that you'd be paying for something
|
|
||||||
that's given away. Get it from the source. Same price everywhere, and here
|
|
||||||
you get to read what you're running.
|
|
||||||
|
|
||||||
## Safety
|
## Safety
|
||||||
|
|
||||||
**These machines contain a CO₂ laser: it burns, blinds, and starts
|
**These machines contain a CO2 laser: it burns, blinds, and starts fires.**
|
||||||
fires.** Never defeat the lid switches or interlock. Never leave a
|
Never defeat the lid switches or the interlock. Never leave a running job
|
||||||
running job unattended. Keep a fire extinguisher within reach. Read [this](https://docs.forgefirm.org/safety/) before you cut
|
unattended. Keep a fire extinguisher within reach. Read
|
||||||
your first job, and read this [regulatory and legal](https://docs.forgefirm.org/install/#regulatory-and-legal) section before installing. [This](https://docs.forgefirm.org/technical/machine/safing-chain/) page describes the hardware safety chain and the software gates ForgeFIRM stacks on it.
|
[Safety](https://docs.forgefirm.org/safety/) before you cut your first job,
|
||||||
|
and [Regulatory and legal](https://docs.forgefirm.org/install/#regulatory-and-legal)
|
||||||
|
before you install.
|
||||||
|
|
||||||
**THIS IS EXPERIMENTAL SOFTWARE
|
**This is experimental software. Use of it could seriously maim or kill you or
|
||||||
Use of this software
|
others, and it may void your warranty. Use it at your own risk.**
|
||||||
could seriously maim or kill you or others, and could void your warranty.
|
|
||||||
Use it at your own risk.**
|
|
||||||
|
|
||||||
This project is not affiliated with or endorsed by Glowforge.
|
This project is not affiliated with or endorsed by Glowforge.
|
||||||
|
|||||||
-1515
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -970,7 +970,7 @@ def cloud_disabled_surface(ctx):
|
|||||||
covers=[("forgectrl", "src/update.*"), ("forgectrl", "src/main.c"), ("forgectrl", "src/ui/wizard.js")],
|
covers=[("forgectrl", "src/update.*"), ("forgectrl", "src/main.c"), ("forgectrl", "src/ui/wizard.js")],
|
||||||
requires=["update.slots-and-signature"],
|
requires=["update.slots-and-signature"],
|
||||||
description="The return itself reboots the machine as a Glowforge, so it never runs from the "
|
description="The return itself reboots the machine as a Glowforge, so it never runs from the "
|
||||||
"catalog (it is a bench drill, logged once per release in CAMPAIGN-LOG). The "
|
"catalog; it is a bench drill, run once per release. The "
|
||||||
"test probes the guards: POST /restore/factory-return without confirm=1 is "
|
"test probes the guards: POST /restore/factory-return without confirm=1 is "
|
||||||
"refused (400) and with confirm=0 too, no update job starts, /slots lists the "
|
"refused (400) and with confirm=0 too, no update job starts, /slots lists the "
|
||||||
"archived factory images, and the setup page carries the footer link's call "
|
"archived factory images, and the setup page carries the footer link's call "
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
# ForgeFIRM - kas build configuration (factory Glowforge control board)
|
# ForgeFIRM - kas build configuration (factory Glowforge control board)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# The forgefirm repo is the BASE: it controls the build, the output firmware
|
# The forgefirm repo is the BASE: it controls the build and the output firmware
|
||||||
# images land here (build/tmp/deploy/images/glowforge/), and the status docs
|
# images land here (build/tmp/deploy/images/glowforge/). The install, build and
|
||||||
# live here (docs/BRINGUP.md, docs/CAMPAIGN-LOG.md). The install, build and
|
|
||||||
# release procedures are on the documentation site: https://docs.forgefirm.org/
|
# release procedures are on the documentation site: https://docs.forgefirm.org/
|
||||||
#
|
#
|
||||||
# Target : Yocto Scarthgap (5.0 LTS) + linux-fslc 6.12 (mainline LTS)
|
# Target : Yocto Scarthgap (5.0 LTS) + linux-fslc 6.12 (mainline LTS)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""De-risk drill for the head-accelerometer crash detector (BRINGUP item 6).
|
"""De-risk drill for the head-accelerometer crash detector.
|
||||||
|
|
||||||
The LIS2HH12 on the head bus (i2c-3 @0x1e) carries an on-chip interrupt
|
The LIS2HH12 on the head bus (i2c-3 @0x1e) carries an on-chip interrupt
|
||||||
generator: a per-axis high-event threshold (IG_THS_X1/Y1/Z1), a duration
|
generator: a per-axis high-event threshold (IG_THS_X1/Y1/Z1), a duration
|
||||||
|
|||||||
@@ -30,6 +30,13 @@
|
|||||||
# bundle. The licenses of the software in the image
|
# bundle. The licenses of the software in the image
|
||||||
# make source necessary, so this is never the
|
# make source necessary, so this is never the
|
||||||
# default.
|
# default.
|
||||||
|
# FORGEFIRM_DOCS_DIR the forgefirm-docs checkout to tag with this
|
||||||
|
# release (default: <repo>/../forgefirm-docs). The
|
||||||
|
# firmware and the documentation that describes it
|
||||||
|
# share a tag, so the documentation that agrees with
|
||||||
|
# a machine can be found from its version.
|
||||||
|
# FORGEFIRM_DOCS_SKIP set to 1 to release without tagging the
|
||||||
|
# documentation. Never the default.
|
||||||
#
|
#
|
||||||
# The source bundle: a release build merges kas/source-bundle.yml, so the
|
# The source bundle: a release build merges kas/source-bundle.yml, so the
|
||||||
# build writes the source of every recipe of the image beside the image.
|
# build writes the source of every recipe of the image beside the image.
|
||||||
@@ -309,6 +316,32 @@ fi
|
|||||||
( cd "$STAGE" && sha256sum $(echo "$ASSETS" | tr ' ' '\n' | grep -v '^sha256sums.txt$') > sha256sums.txt )
|
( cd "$STAGE" && sha256sum $(echo "$ASSETS" | tr ' ' '\n' | grep -v '^sha256sums.txt$') > sha256sums.txt )
|
||||||
ls -la "$STAGE"
|
ls -la "$STAGE"
|
||||||
|
|
||||||
|
# --- the documentation tag ----------------------------------------------------
|
||||||
|
#
|
||||||
|
# Firmware on a machine needs the documentation that agrees with it, so the
|
||||||
|
# docs repository carries the same tag as the release. The tag is made here and
|
||||||
|
# pushed with the release, never before: a tag on documentation that never
|
||||||
|
# shipped is worse than no tag at all.
|
||||||
|
DOCS_TAG_CMD=""
|
||||||
|
if [ -n "${FORGEFIRM_DOCS_SKIP:-}" ]; then
|
||||||
|
warn "docs tag SKIPPED by FORGEFIRM_DOCS_SKIP - this release ships no matching documentation tag"
|
||||||
|
else
|
||||||
|
DOCS_DIR="${FORGEFIRM_DOCS_DIR:-$REPO/../forgefirm-docs}"
|
||||||
|
[ -d "$DOCS_DIR/.git" ] \
|
||||||
|
|| die "no forgefirm-docs checkout at $DOCS_DIR (set FORGEFIRM_DOCS_DIR, or FORGEFIRM_DOCS_SKIP=1 to release without one)"
|
||||||
|
[ -z "$(git -C "$DOCS_DIR" status --porcelain)" ] \
|
||||||
|
|| die "forgefirm-docs has uncommitted changes; commit them before a release"
|
||||||
|
if git -C "$DOCS_DIR" rev-parse -q --verify "refs/tags/v$VERSION" >/dev/null 2>&1; then
|
||||||
|
echo "docs: tag v$VERSION already exists in $DOCS_DIR"
|
||||||
|
else
|
||||||
|
git -C "$DOCS_DIR" tag -a "v$VERSION" -m "ForgeFIRM v$VERSION" \
|
||||||
|
|| die "cannot tag forgefirm-docs"
|
||||||
|
echo "docs: tagged $DOCS_DIR at v$VERSION"
|
||||||
|
fi
|
||||||
|
echo "docs: v$VERSION -> $(git -C "$DOCS_DIR" rev-parse --short HEAD)"
|
||||||
|
DOCS_TAG_CMD="git -C $DOCS_DIR push origin v$VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
|
||||||
== release v$VERSION staged ==
|
== release v$VERSION staged ==
|
||||||
@@ -317,12 +350,16 @@ Pre-publish checklist (docs.forgefirm.org, Developers, "Release flow"):
|
|||||||
- meta-openglow pushed; kas config flipped to the pinned-remote block
|
- meta-openglow pushed; kas config flipped to the pinned-remote block
|
||||||
- kas lock refreshed
|
- kas lock refreshed
|
||||||
- self-containment proven from a fresh clone
|
- self-containment proven from a fresh clone
|
||||||
|
- forgefirm-docs current for this release (the currency rule) and pushed
|
||||||
|
|
||||||
Publish (from a directory with an authenticated gh):
|
Publish (from a directory with an authenticated gh):
|
||||||
cd "$STAGE"
|
cd "$STAGE"
|
||||||
gh release create "v$VERSION" --repo openglow-org/forgefirm \\
|
gh release create "v$VERSION" --repo openglow-org/forgefirm \\
|
||||||
--title "ForgeFIRM v$VERSION" --generate-notes $PRERELEASE \\
|
--title "ForgeFIRM v$VERSION" --generate-notes $PRERELEASE \\
|
||||||
$ASSETS
|
$ASSETS
|
||||||
|
|
||||||
|
Push the documentation tag with it:
|
||||||
|
$DOCS_TAG_CMD
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
if [ "$PUBLISH" = "1" ]; then
|
if [ "$PUBLISH" = "1" ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user