diff --git a/BUILD.md b/BUILD.md index 24f85d1..bdeb4e0 100644 --- a/BUILD.md +++ b/BUILD.md @@ -70,6 +70,15 @@ kas lock kas/forgefirm-glowforge.yml See [`kas/README.md`](kas/README.md) for details, the container-build option, and the Scarthgap migration backlog. +## Third-party firmware licensing + +The i.MX6 BSP installs NXP firmware blobs (VPU, EPDC) that are distributed +under NXP's firmware EULA. The build config accepts it +(`ACCEPT_FSL_EULA = "1"`), and the image ships the license text alongside the +blobs at `/usr/share/licenses/firmware-imx/EULA` — keep it there in any +redistributed image. The SDMA firmware comes from `linux-firmware`, which +carries its own license package. + ## Write to an SD card ```console diff --git a/README.md b/README.md index 331a5da..a79cc7e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,60 @@ -# OpenGlow/ForgeFIRM Firmware for Glowforge -This repository contains opensource firmware for Glowforge brand CNC lasers. - -* [Latest Release](https://github.com/ScottW514/forgefirm/releases) -* [Installation Instructions](https://github.com/ScottW514/forgefirm/blob/master/INSTALL.md) -* [Build Instructions](https://github.com/ScottW514/forgefirm/blob/master/BUILD.md) -* [Community Support](https://community.openglow.org) - -# Roadmap -* Base image that supports all Glowforge hardware (complete) -* Support Glowforge web service - Python implementation (mostly complete) -* REST API w/ G-Code support (~~in progress~~) Stalled -* Browser-based GUI (~~pending~~) Stalled - -This project is for experimental purposes only, and is not supported or endorsed by Glowforge. +# OpenGlow/ForgeFIRM Firmware for Glowforge + +Open-source firmware for Glowforge brand CNC lasers. ForgeFIRM replaces the +cloud-dependent factory software on the **stock control board** — no hardware +modification — and gives the machine a local controller, a local web control +panel, and a standard Grbl interface. + +* [Latest Release](https://github.com/ScottW514/forgefirm/releases) +* [Installation Instructions](https://github.com/ScottW514/forgefirm/blob/master/INSTALL.md) +* [Build Instructions](https://github.com/ScottW514/forgefirm/blob/master/BUILD.md) +* [Connecting LightBurn](https://github.com/ScottW514/forgefirm/blob/master/docs/LIGHTBURN.md) +* [Community Support](https://community.openglow.org) + +## What it does + +**Two controller modes, selected in the web panel and switchable while the +machine is idle:** + +* **GRBL mode** — [grblHAL](https://github.com/grblHAL) runs on the machine and + speaks 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 (SDMA + + EPIT), fed live by the planner. M3/M4 dynamic laser power, coolant-flow + verification, over-temp holds, and an operator button press to arm the laser + for each job. +* **Cloud mode** — the machine presents itself as a stock Glowforge to the + Glowforge web service, so the phone and web apps work as they always did. + Optional, and off by default; nothing about GRBL mode needs it. + +**Around both modes:** + +* A **web control panel** on port 8080: machine status and position, coolant + and fan telemetry, safety-switch states, camera view, machine settings, + hardware diagnostics, firmware updates, and boot-slot management. +* Both **cameras** as MJPEG streams and full-resolution snapshots — the lid + camera feeds LightBurn's camera overlay directly. +* **Camera-referenced homing**: `$H` from any sender runs the factory-style + camera homing cycle through the Glowforge service, and the machine records + where it is. +* **Installs alongside the factory firmware** in the unused A/B rootfs slot, + archiving every factory version first, so the machine can be switched back + to stock at any time without the Glowforge cloud. + +## Hardware + +The control board is common to Glowforge Basic, Plus, and Pro. The 5 MP +(OV5648) camera modules are fully supported; the 8 MP (OV8856) modules found in +"HD" units bind but do not capture yet — see the camera note in +[kas/README.md](kas/README.md). + +## Roadmap + +* Limit-switch homing as an alternative to camera-referenced homing. +* Camera lens calibration and bed alignment for the LightBurn overlay. +* Capture support for the 8 MP (OV8856) camera modules. +* Cloud mode: stream jobs into the motion ring during the run, lifting the + job-length cap that buffering the whole job imposes. + +**A very important warning: this is experimental software. Use of this software +could seriously maim or kill you or others, and voids your warranty. It is not +affiliated with or endorsed by Glowforge. Use it at your own risk.** diff --git a/docs/LIGHTBURN.md b/docs/LIGHTBURN.md index 8c1d51f..9fedf7d 100644 --- a/docs/LIGHTBURN.md +++ b/docs/LIGHTBURN.md @@ -1,8 +1,6 @@ # LightBurn setup & operation (ForgeFIRM) -Status: laser software implemented, **first light pending** (see -BRINGUP.md for the commissioning record). The laser fires only inside -an operator-armed window: +The laser fires only inside an operator-armed window: - **Starting a job that fires: press the button.** At the first laser-on command of a job the machine unlocks its laser latch, diff --git a/kas/README.md b/kas/README.md index 483c50e..547072b 100644 --- a/kas/README.md +++ b/kas/README.md @@ -89,11 +89,13 @@ kas auto-loads the lockfile on subsequent runs. Commit it; refresh deliberately. The build is only reproducible when recipe pins, layer branches, and the kas config move in the right order. The sequence, with current status: -1. **Source repos pushed & pinned** — **DONE.** - `kernel-module-glowforge` (`029dfb6`) and `python3-gfhardware` (`9bf31fd`) - pushed to GitHub master; both recipes in meta-openglow pin those exact - SRCREVs (no `AUTOREV` anywhere). Whenever either repo changes: push it, - then bump the recipe `SRCREV` in meta-openglow deliberately. +1. **Source repos pushed & pinned** — **DONE.** Every source repo + (`kernel-module-glowforge`, `python3-gfhardware`, `Glowforge-Utilities`, + `grblHAL-glowforge`, `forgectrl`) is on GitHub and its recipe pins an exact + `SRCREV` — no `AUTOREV` anywhere. Whenever a source repo changes: push it, + then bump the recipe `SRCREV` deliberately (BSP recipes in meta-openglow, + ForgeFIRM components in meta-forgefirm) and re-verify with + `bitbake -c fetch `. 2. **meta-openglow pushed** — **DONE.** The Scarthgap port lives on the **`scarthgap` branch** (Yocto layer convention; the Dunfell-era `master` is untouched). Development continues on the local sibling checkout; push / @@ -104,9 +106,6 @@ config move in the right order. The sequence, with current status: 4. **At release time**: - flip `meta-openglow` in `forgefirm-glowforge.yml` from the local-sibling block to the pinned-remote block (commented FUTURE block in the file); - - drop meta-openglow's `kernel-module-glowforge.bbappend` (externalsrc to - the local sibling — its perl-native DEPENDS is already carried in the - base recipe) so a fresh clone is fully self-contained; - refresh `kas lock`, tag all repos, and prove self-containment by building from a **fresh clone**. 5. **GitHub release**: run `scripts/release.sh ` on the build @@ -120,8 +119,11 @@ config move in the right order. The sequence, with current status: `v` = `FORGEFIRM_RELEASE` = the rootfs `/etc/forgefirm-version` = the `.fw` meta-version; `release.sh` enforces the agreement. -For gfhardware development, either bump the recipe pin per iteration or add a -tracked externalsrc bbappend mirroring the kernel-module pattern. +All recipes fetch their pinned revision from GitHub, so an image build is +reproducible from the repos alone. For fast iteration on a source repo, bump +its pin per iteration, or add a **local, untracked** `externalsrc` bbappend +pointing at the sibling checkout — never commit one, or released images stop +matching the pins. ## Scarthgap migration backlog @@ -145,8 +147,9 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until: `st,lis2hh12` ×3 + `national,lm75b` + `ti,wl1805` + gpio keys/leds bind to mainline drivers; `reg-userspace-consumer` enabled via `glowforge.cfg`. (SPI-delay / PWM-prescaler dispositions are under Motion polish below.) - - **Motion path — DONE (builds clean; runtime needs hardware).** The whole - chain forward-ports and compiles on 6.12: + - **Motion path — DONE and hardware-validated** (live-fed pulse stream, + real gantry motion, laser fire). The whole chain forward-ports and + compiles on 6.12: - **EPIT API**: `epit_api.c` in `arch/arm/mach-imx` (`CONFIG_MXC_EPIT_API`), in vmlinux, symbols exported; `&epit1/&epit2` in the DT. - **SDMA-expose**: re-created `dma-imx-sdma.h` + `0003-imx-sdma-*.patch` @@ -166,16 +169,22 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until: `spi_imx_setupxfer` (write `MX51_ECSPI_PERIODREG` from `t->word_delay`, guarded to ECSPI) and verify the wait-states on a scope. pic.c keeps the inter-transfer delay meanwhile. `glowforge,imx-pwm-audio` (buzzer) deferred. - - **Camera — DONE (builds clean; runtime needs hardware).** The factory + - **Camera — DONE and hardware-validated.** The factory `ov5648_mipi.c` (NXP's removed `v4l2_int_device`/`mxc_v4l2_capture`) is replaced by the mainline `ovti,ov5648` subdev + imx6 `imx-media` (IPU CSI) + `imx6-mipi-csi2` receiver. The factory CAM_SEL MIPI switch is modelled - with the mainline `video-mux` (gpio-mux on `gpio7 10`): both `ov5648`s → - video-mux → `mipi_csi` → IPU CSI. Sensor `xvclk` (25 MHz fixed-clock) + - avdd/dovdd/dvdd rails in the DT. All drivers build as modules and - `glowforge.dtb` compiles with the full pipeline. **HW bring-up:** confirm - the real supply rails, CSI-2 lane count/order and CAM_SEL polarity, then - validate with `media-ctl` + a v4l2 capture. + with the mainline `video-mux` (gpio-mux on `gpio7 10`): both sensors → + video-mux → `mipi_csi` → IPU CSI. Sensor `xvclk` is the board's 24 MHz + fixed oscillator (matching the factory DTB); avdd/dovdd/dvdd rails are in + the DT. Both cameras stream live through forgectrl (MJPEG at 15 fps with + VPU JPEG encode, full-resolution snapshots, mux arbitration). + **HD-unit caveat:** the DT lists both `ovti,ov5648` (5 MP) and + `ovti,ov8856` (8 MP) at 0x36 so one image covers both, and the driver + matching the chip ID wins — but mainline `ov8856` expects a 19.2 MHz + xvclk and only warns at 24 MHz, and the capture path is written to + ov5648's SBGGR8 2592×1944 format set. 8 MP "HD" modules therefore bind + but do not capture; adding 24 MHz PLL modes plus a sensor-aware capture + path is the open work. 3. **u-boot** — **DONE.** The `glowforge` u-boot is a standalone `u-boot_2020.01.bb` (Scarthgap's poky has no u-boot 2020.01 base recipe to extend). It reuses poky's @@ -186,8 +195,11 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until: `fw_printenv`/`fw_setenv` from `u-boot-fw-utils` to `libubootenv` (`PREFERRED_PROVIDER_u-boot-fw-utils` in `glowforge.inc`) when the rootfs needs them. -4. **Device tree** — revalidate the `glowforge` `.dts` against the linux-fslc - 6.12 DT bindings (paired with the kernel forward-port in #2). +4. **Device tree — DONE.** The `glowforge` `.dts` is validated against the + linux-fslc 6.12 bindings and against the running board (motion, safety + readbacks, cameras, sensors all bind and work). Residue: `control_12v` + still uses the `reg-userspace-consumer` compatible, which matches no 6.12 + driver — convert it to `regulator-output` or drop the node. 5. **Real-time strategy — decided.** The kernel runs `CONFIG_PREEMPT=y` (factory behavior; `imx_v6_v7_defconfig` alone gives only `PREEMPT_VOLUNTARY`). **PREEMPT_RT is not selectable on arm32 6.12** (no @@ -200,14 +212,14 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until: the underrun bench ever contradicts this arithmetic. (Bench: 5 s of continuous feed at a 1 s buffer depth, zero underruns.) -6. **gfui-client → forgectrl** — the Glowforge **cloud client is excluded** +6. **gfui-client → forgectrl — DONE.** The stock `gfui-client` is excluded from `forgefirm-image` (`IMAGE_INSTALL:remove = "gfui-client"` in - `meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb`) — it connected to - Glowforge's servers, the dependency ForgeFIRM exists to cut. Its slot is - filled by `forgectrl` (github.com/ScottW514/forgectrl — the ForgeFIRM - control daemon; camera MJPEG service today, hardware status/control and - GRBL-vs-cloud mode selection planned) plus the `grblhal-glowforge` - motion controller (both in the images with boot autostart). + `meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb`). Its slot is + filled by `forgectrl` (github.com/ScottW514/forgectrl — the machine-services + daemon: web control panel, cameras, telemetry, settings, diagnostics, + cooling engine, updates, and controller-mode supervision) plus the two + controllers it supervises, `grblhal-glowforge` (Grbl over TCP:23) and + `gfcloud` (the optional Glowforge web-service client, off unless selected). --- @@ -215,6 +227,7 @@ Scarthgap, but the legacy (Dunfell/Gatesgarth) layers won't build clean until: stack and deploys `forgefirm-image-glowforge.rootfs.wic.gz` (+ `zImage`, `glowforge.dtb`, `u-boot-glowforge.imx`) under `build/tmp/deploy/images/glowforge/`. Build-time prerequisites baked into the config: `ACCEPT_FSL_EULA = "1"` (NXP -firmware-imx) and the kernel default in `glowforge.conf`. Everything compiles; -on-hardware bring-up (motion timing, laser/safety chain, camera pipeline) is the -remaining validation and needs a real board. +firmware-imx — the image also installs `firmware-imx-lic` so the EULA text +ships beside the blobs) and the kernel default in `glowforge.conf`. The stack +is hardware-validated end to end: motion timing, the laser and safety chain, +the camera pipeline, both controller modes, and the A/B install path. diff --git a/meta-forgefirm/recipes-bsp/firmware-imx/firmware-imx_%.bbappend b/meta-forgefirm/recipes-bsp/firmware-imx/firmware-imx_%.bbappend new file mode 100644 index 0000000..fef16b6 --- /dev/null +++ b/meta-forgefirm/recipes-bsp/firmware-imx/firmware-imx_%.bbappend @@ -0,0 +1,13 @@ +# NXP distributes the i.MX VPU/EPDC firmware blobs under its firmware EULA +# (accepted with ACCEPT_FSL_EULA), so an image carrying those blobs has to +# carry the license text with them. +# +# LICENSE_CREATE_PACKAGE builds the license package, but license.bbclass adds +# it to PACKAGES during do_package - too late for an image to depend on the +# name. Declaring it here makes it resolvable at parse time; the class then +# logs one "package already existed" note for this recipe, which is why FILES +# is set here as well. +LICENSE_CREATE_PACKAGE = "1" + +PACKAGES:prepend = "${PN}-lic " +FILES:${PN}-lic = "${datadir}/licenses/${PN}" diff --git a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb index 4752123..d2ccf43 100644 --- a/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb +++ b/meta-forgefirm/recipes-forgefirm/images/forgefirm-image.bb @@ -25,6 +25,11 @@ IMAGE_INSTALL:remove = "gfui-client" # slotmigrate: boot-time reclaim of the legacy p4 layout (grows /data). IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome gfcloud v4l-utils fwup ffboot slotmigrate" +# NXP's firmware EULA covers the i.MX VPU/EPDC blobs the BSP installs, so the +# image ships the license text with them (/usr/share/licenses/firmware-imx). +# The SDMA firmware brings its own -license package through linux-firmware. +IMAGE_INSTALL:append = " firmware-imx-lic" + # The release rootfs must fit a 200 MiB factory eMMC slot (409600 blocks). # Sizing: content + 40 MiB working space, hard-capped at the slot size — # the build fails rather than emit an unflashable image. The raw ext4 is