Building a Fully Automated Filament Tracking Ecosystem

Managing a print farm means managing consumables at scale. Filament arrives by the kilogram, gets loaded onto machines, gets consumed across dozens of jobs, and eventually runs out — often in the middle of a print if you’re not careful. The same stock serves two masters simultaneously: it feeds the printers as a production consumable, and it sits in the WooCommerce catalogue as product inventory available for resale. A spool that gets loaded onto a machine is a spool that’s no longer available to ship to a customer. For a commercial operation, the difference between knowing exactly what you have and guessing is the difference between quoting accurately and eating the cost of a failed job — or overselling stock that’s already on a print bed.

This post describes the end-to-end system we built at 3D ETPLUS to track filament from purchase invoice to printer nozzle to WooCommerce stock level, using a combination of open-source tools, cheap hardware, custom software bridges, and — for the initial bulk import — a language model that read invoices and wrote its own API calls.

The system has five layers, each solving a distinct problem:

  1. Spoolman as the central inventory database, extended to understand multiple NFC tag formats
  2. NFC readers on every printer that link physical spools to the software inventory the moment they’re loaded
  3. A Moonraker fork that tracks filament consumption per extruder for multi-tool machines without requiring any macro changes
  4. A Snapmaker bridge that brings a proprietary-firmware IDEX printer into the same ecosystem as our Klipper machines
  5. A sync script that translates Spoolman consumption data into real-time WooCommerce stock levels on the production website

The Foundation: Spoolman with NFC Support

Spoolman is a self-hosted web service for tracking filament spools. Out of the box it integrates with Klipper/Moonraker and OctoPrint, gives you a REST API, and lets you report filament consumption during prints. The upstream project is solid — we use it as the spine of the system — but it doesn’t know how to read the NFC tags that manufacturers increasingly embed in their filament packaging.

Our Spoolman fork adds NFC spool identification at both the browser and server level, with support for three tag formats that cover most of the market:

TigerTag (ISO 14443A / NTAG213) is a binary format used by Bambu Lab filament and a growing number of third-party manufacturers. The tag stores a product identifier; the actual filament specifications live in TigerTag’s external database, which the fork queries at scan time to retrieve material type, color, diameter, and density. An unrecognized TigerTag triggers automatic spool creation from the database lookup — scan a new spool and it appears in Spoolman without any manual entry.

OpenPrintTag (ISO 15693 / NFC-V) is Prusa’s open NDEF/CBOR standard. Tags carry per-spool UUIDs and embed the filament data directly on the chip, making them self-contained without requiring an external lookup.

Qidi (ISO 14443A / MIFARE Classic 1K) covers filament from Qidi printers. The tag format encodes material type and color data in Qidi’s proprietary sector layout.

The fork exposes two scanning interfaces. The browser-based path uses the Web NFC API — supported on Chrome for Android — so anyone with a compatible phone can tap a spool and have it appear in Spoolman. The server-side path uses a USB NFC reader connected to the Spoolman host, enabling scanning from a fixed station. Both paths share the same identification and auto-creation logic.


Bootstrapping 300 kg of Inventory with an LLM

Before any of the NFC scanning was useful, we needed the existing inventory in Spoolman. We had received a single large bulk order from one manufacturer — roughly 300 kg of filament, one brand, across multiple materials and colors — and that stock existed only as line items on a purchase invoice, not in any structured database.

Entering this manually would have taken hours and been error-prone. Instead, we fed the invoice PDFs to Claude and described what we needed: for each line item, identify the filament brand, material, color, diameter, and quantity; look up the manufacturer’s datasheet to fill in density and other technical specifications; then generate the appropriate Spoolman REST API calls to create spool entries.

The process worked better than expected. The LLM correctly parsed invoice line items across multiple supplier formats, found datasheets on manufacturer websites for the specific materials listed, and produced valid curl POST requests targeting our Spoolman instance. Some entries needed minor corrections — a density value that differed between the datasheet and a community measurement, a color name that the datasheet called something slightly different than the invoice — but the bulk of the inventory was imported in a single session.

The practical lesson: an LLM combined with a structured REST API is a capable ETL pipeline for semi-structured documents. The invoice is the source, the datasheet is the enrichment, the API is the target, and the LLM handles the transformation. No custom script needed, no intermediate format to maintain.


NFC at the Printer: klipper-nfc-daemon

Getting a spool into Spoolman is only half the problem. The other half is telling the printer — and Spoolman — which spool is actually loaded. Without this link, Klipper and Moonraker have no way to know which spool’s weight to decrement when filament is consumed.

klipper-nfc-daemon runs as a service on the Klipper host and handles this link automatically. Our Klipper machines run on Intelligent Agent’s Recore controller board, a purpose-built ARM board that replaces the Raspberry Pi + MCU combination with a single unit. The NFC hardware is a PN532 reader connected via a USB-UART cable. At under $10 per printer, it’s cheap enough to deploy across an entire fleet. The PN532 reads NTAG213 and NTAG215 tags — covering TigerTag format — reliably at short range.

When a spool is loaded, the operator taps it against the reader. The daemon reads the tag, queries Spoolman’s NFC endpoint to find the matching spool record, and links that spool to the active tool in Spoolman. From that point, every millimeter of extrusion Klipper reports is attributed to the correct spool.

For printers with multiple extruders — an IDEX machine, a tool changer, a multi-material system — the daemon presents a UI dialog asking which tool the scanned spool is being loaded into. The operator selects the tool, and the daemon completes the link for that specific extruder. This interaction is minimal: tap the spool, pick the tool if prompted, done.

One hardware note: the PN532 over USB-UART only handles Type A tags (NTAG213/215). OpenPrintTag uses NFC-V (Type V / ISO 15693), which requires a different reader chipset. For a fleet that mixes TigerTag and OpenPrintTag filament, the server-side browser scanning path in the Spoolman fork handles NFC-V via a phone, while the PN532 handles TigerTag at the printer. This is a workable split; a single reader that handled both formats would be cleaner.

The cost economics favour NTAG213 heavily. Blank NTAG213 chips run around $0.02 per unit at 100-unit quantities, making them essentially free at fleet scale. NFC-V tags are significantly more expensive. Crucially, NTAG213 tags are rewritable — the same physical tag can be erased and reassigned to a new spool when one runs out, which means the tag pool amortises across the entire life of the fleet rather than being consumed one-per-spool.


Multi-Tool Filament Tracking: The Moonraker Fork

Klipper’s upstream Moonraker handles Spoolman integration well for single-extruder machines. For IDEX and multi-tool printers, the upstream implementation tracks only one active spool — reflecting an assumption in the original API design that there is one active filament at any moment.

Our Moonraker fork extends the Spoolman integration to track one spool per tool, without requiring any changes to printer macros. The approach is purely at the API layer.

The key changes:

Per-tool spool storage. Rather than a single spoolman.spool_id database key, the fork stores spoolman.spool_id.0, spoolman.spool_id.1, and so on — one key per tool. Existing single-extruder configurations migrate automatically on first startup.

Tool-aware API endpoints. The GET/POST /server/spoolman/spool_id HTTP endpoint and the corresponding WebSocket RPC handlers accept an optional tool parameter. This matches the API shape that Mainsail’s multi-extruder Spoolman panel expects, so the UI correctly displays which spool is loaded in which tool.

tool_spool_map response field. Mainsail uses this field to switch from single-spool display mode to per-tool display mode. Without it, Mainsail falls back to single-spool behavior regardless of what’s configured on the backend. Adding it took one response field; the UI behavior change was immediate.

The result is that a Klipper machine with two, four, or more tools displays accurate per-tool spool assignments in Mainsail, and filament consumption is debited from the correct spool as each tool extrudes. No macros. No ACTIVATE_EXTRUDER calls in start GCode. The tracking happens at the Moonraker layer, below the GCode.

This fork is the upstream PR we submitted — the commits are clean, the test coverage is there, and the changes are minimal. Whether it lands in upstream Moonraker depends on maintainer bandwidth. In the meantime, running our fork means the functionality is available without waiting.


Bringing the Snapmaker J1S into the Ecosystem

Not everything on the floor runs Klipper. Our Snapmaker J1S is an IDEX printer with capable hardware — independent dual-extrusion, a decent build volume, reliable mechanics — but it runs Snapmaker’s proprietary firmware and speaks a binary protocol called SACP. From the perspective of our Mainsail-based workflow, it was an island.

snapmaker-moonraker is the bridge we built to connect it. The bridge runs alongside a Klipper/Moonraker instance and translates between the Moonraker API that Mainsail, PrusaSlicer, and Spoolman expect and the SACP binary protocol the J1S actually speaks.

The integration is deep enough that the J1S is now indistinguishable from a Klipper machine at the tooling layer:

  • Files are uploaded from PrusaSlicer directly to Mainsail, which forwards them through the bridge to the printer
  • A GCode post-processor rewrites tool numbers, generates the Snapmaker V1 metadata header the touchscreen requires, extracts slicer thumbnail data and converts it to the format the J1S HMI displays, and handles unused-nozzle shutoff for single-material dual-extruder jobs
  • Print control (start, pause, resume, cancel) goes through native SACP commands, keeping the touchscreen HMI in sync
  • Temperature reporting and control, fan control, and print progress are all exposed via the Moonraker API and visible in Mainsail
  • Spoolman integration tracks filament consumption per extruder, using the same per-tool spool mechanism as our other multi-extruder machines
  • IDEX Copy and Mirror mode work: PrusaSlicer signals the mode via M605, the post-processor embeds it in the Snapmaker V1 header, and the bridge sends the corresponding SACP command before print start

The bridge ships with 12 PrusaSlicer printer profiles covering all three IDEX modes across eight quality presets, so a new installation goes from zero to printing in any mode without manual slicer configuration.

The design principle throughout was to push all the adaptation into the bridge and leave the surrounding ecosystem unchanged. Mainsail doesn’t know it’s talking to a Snapmaker. Spoolman doesn’t know it’s tracking a proprietary machine. PrusaSlicer uses the same dual-extruder configuration it uses for any other IDEX printer. The bridge absorbs the complexity so nothing else has to.

A detailed technical writeup of the bridge’s development — including the GCode post-processor architecture, the SACP protocol discoveries, the dual-extruder tracking implementation, and the IDEX mode coordination — is available in our earlier blog post.


Closing the Loop: From Spoolman to WooCommerce

The last piece connects the inventory system to the storefront. The same filament stock that feeds the printers is also listed for resale — which means every print job is a potential stock-level event. Keeping listed quantities accurate matters in both directions: overselling means backorders on stock that’s already sitting in a nozzle, and underselling means missed revenue on material that’s sitting on a shelf.

The source of truth for filament inventory is Spoolman. As spools are used across the fleet — consumed by print jobs tracked through Klipper, the Snapmaker bridge, or recorded manually — Spoolman reflects the updated remaining weight on each spool.

A cron job running on the staging server handles the reconciliation:

  1. Poll Spoolman for all spool records. Identify spools that have been used — either by the presence of a last_used timestamp or by a remaining weight below the original spool weight.

  2. Calculate available stock per filament product (brand, material, color, diameter), aggregating remaining weight across all matching spools.

  3. Query the staging WooCommerce store to verify its current stock levels match the Spoolman data. This catches any drift between the two systems before pushing to production.

  4. Push to production. Once staging inventory is reconciled, the script updates the production WooCommerce store’s stock levels via the REST API.

The staging-first design matters here. The production website is live and customer-facing; pushing bad data directly from Spoolman to production without a verification step risks inaccurate stock levels caused by data anomalies, API errors, or partially-completed spool records. Staging acts as a checkpoint.

The end state: when a print job finishes and Spoolman records the filament consumed, that consumption flows through the cron job into the product stock levels visible to customers on the website. The latency is one cron cycle — typically under an hour. For a business where filament stock is both the product and the consumable, this automatic reconciliation removes an entire category of manual bookkeeping.


The System as a Whole

Each piece was built to solve a specific problem, but they compose into something larger than their parts:

A new spool arrives. The invoice PDF goes to the LLM, which creates the Spoolman entry. The spool gets an NFC tag if it doesn’t already have one.

The spool gets loaded onto a printer. The operator taps it against the PN532 reader. The daemon links the physical spool to the correct tool in Spoolman. If it’s a multi-extruder machine, they select which tool. The whole interaction takes five seconds.

A job runs. Klipper (or the Snapmaker bridge) reports extrusion in real time. Moonraker debits the correct spool. If it’s a dual-extruder job, both spools are tracked independently.

The job finishes. Spoolman has updated remaining weights. Within the hour, the cron job picks up the change, verifies against staging, and updates the WooCommerce product stock.

No spreadsheets. No manual stock counts. No end-of-day reconciliation. The inventory state is continuous and accurate because every consumption event is captured at the point it occurs.


What’s Open, What’s Not

All four software components are open source:

The WooCommerce sync script is specific to our shop setup and not currently published, but the approach — Spoolman REST API in, WooCommerce REST API out, with a staging verification step — is straightforward to replicate.

Hardware cost for a single printer integration is under $10 for the PN532 reader and USB-UART cable. The software stack runs on the same Raspberry Pi that runs Klipper, so there’s no additional compute to provision.

This was built with substantial assistance from Claude (Anthropic). The LLM was involved at every layer: writing the initial protocol implementations, debugging binary parsing, generating the spool import curl calls from invoice PDFs, and authoring the posts that describe it. The AI disclosure in the snapmaker-moonraker repository applies to this entire ecosystem.

Follow
Newest Art:
SHOPPING BAG 0
RECENTLY VIEWED 0