From b27829bde9bcf6a1923f309a059fd05cf75aaf84 Mon Sep 17 00:00:00 2001 From: Joost MF Liebregts Date: Thu, 22 Jan 2026 20:56:02 +0100 Subject: [PATCH] feat: Add spool_id extraction from OpenSpool NFC tags - Extract optional spool_id field from OpenSpool JSON payload - Call ON_NFC_SPOOL_READ macro when spool_id is present - Add Spoolman Integration section to documentation - Enables automatic spool tracking via NFC tags - Backward compatible: tags without spool_id work as before Co-Authored-By: Claude Opus 4.5 --- PR_BODY.md | 57 +++++++++++++++ docs/rfid_support.md | 73 +++++++++++++++++++ .../13-rfid-support/examples/nfc_spoolman.cfg | 25 +++++++ .../patches/02-add-ndef-protocol.patch | 13 +++- .../klippy/extras/filament_protocol_ndef.py | 3 + 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 PR_BODY.md create mode 100644 overlays/firmware-extended/13-rfid-support/examples/nfc_spoolman.cfg diff --git a/PR_BODY.md b/PR_BODY.md new file mode 100644 index 00000000..46c22eaf --- /dev/null +++ b/PR_BODY.md @@ -0,0 +1,57 @@ +## Summary + +Your RFID filament detection implementation is excellent - thank you for the solid NDEF/OpenSpool support! + +This PR extends it with optional Spoolman integration by: +- Extracting the `spool_id` field from OpenSpool JSON payloads +- Calling an optional `ON_NFC_SPOOL_READ` macro when `spool_id` is present + +This allows users with [Spoolman](https://github.com/Donkie/Spoolman) to get automatic spool tracking when loading filament. + +## Changes + +- **filament_protocol_ndef.py**: Extract optional `spool_id` field from OpenSpool JSON into `info['SPOOL_ID']` +- **02-add-ndef-protocol.patch**: Call `ON_NFC_SPOOL_READ CHANNEL= SPOOL_ID=` macro when spool_id is present +- **docs/rfid_support.md**: Add Spoolman Integration section with field reference and example macro +- **examples/nfc_spoolman.cfg**: Ready-to-use macro file users can copy to their config + +## Tag Format + +Users add `spool_id` to their existing OpenSpool tags: + +```json +{ + "protocol": "openspool", + "version": "1.0", + "brand": "Elegoo", + "type": "PLA", + "color_hex": "#FF5733", + "spool_id": 42 +} +``` + +## Backward Compatibility + +- Tags without `spool_id` work exactly as before +- The macro call only fires when `spool_id` is present +- No configuration required unless user wants Spoolman integration + +## Example Macro + +Users create `extended/klipper/nfc_spoolman.cfg`: + +```cfg +[gcode_macro ON_NFC_SPOOL_READ] +gcode: + {% set channel = params.CHANNEL|int %} + {% set spool_id = params.SPOOL_ID|int %} + {% set tool = "T" ~ channel %} + SET_GCODE_VARIABLE MACRO={tool} VARIABLE=spool_id VALUE={spool_id} + RESPOND PREFIX="NFC" MSG="Spool {spool_id} assigned to {tool}" +``` + +## Testing + +- Tested with NTAG215 tags containing OpenSpool JSON with spool_id field +- Verified backward compatibility with tags without spool_id +- Verified macro is called with correct channel and spool_id parameters diff --git a/docs/rfid_support.md b/docs/rfid_support.md index 2c5cf2e3..00fad350 100644 --- a/docs/rfid_support.md +++ b/docs/rfid_support.md @@ -102,6 +102,7 @@ Using the non-standard OpenSpool `subtype` field it is possible to specify a mat - `additional_color_hexes` - Additional colors for multicolor spools (up to 4) - `weight` - Spool weight in grams - `diameter` - Filament diameter in mm (e.g., 1.75) +- `spool_id` - Spoolman spool ID for automatic spool tracking (see [Spoolman Integration](#spoolman-integration)) ### Snapmaker Orca Naming Convention @@ -135,3 +136,75 @@ Use the **NFC Tools** app (iOS/Android) to inspect tags: **NTAG tags only work on extended firmware:** - Basic and original firmware only support Mifare Classic 1K with Snapmaker proprietary format - Extended firmware adds NTAG215/216 support + +## Spoolman Integration + +[Spoolman](https://github.com/Donkie/Spoolman) is a self-hosted filament inventory management system. When combined with NFC tags, you can automatically track which spool is loaded in each extruder. + +### How It Works + +1. Include `spool_id` in your OpenSpool NFC tag payload +2. When the tag is read, the firmware calls the `ON_NFC_SPOOL_READ` macro +3. Your macro updates Spoolman with the active spool + +### Tag Format + +Add the `spool_id` field to your OpenSpool payload: + +```json +{ + "protocol": "openspool", + "version": "1.0", + "brand": "Elegoo", + "type": "PLA", + "color_hex": "#FF5733", + "min_temp": 200, + "max_temp": 220, + "spool_id": 42 +} +``` + +The `spool_id` should match the spool ID in your Spoolman database. + +### Example Macro + +Create `extended/klipper/nfc_spoolman.cfg`: + +```cfg +# NFC Tag → Spoolman Integration +# Automatically called when an NFC tag with spool_id is read +# Parameters: CHANNEL (0-3 = T0-T3), SPOOL_ID (integer) + +[gcode_macro ON_NFC_SPOOL_READ] +description: Called when NFC tag with spool_id is read +gcode: + {% set channel = params.CHANNEL|int %} + {% set spool_id = params.SPOOL_ID|int %} + {% set tool = "T" ~ channel %} + + # Update tool's spool assignment + SET_GCODE_VARIABLE MACRO={tool} VARIABLE=spool_id VALUE={spool_id} + + # Optional: Trigger deferred spool save + UPDATE_DELAYED_GCODE ID=SAVE_SELECTED_SPOOLS DURATION=1 + + RESPOND PREFIX="NFC" MSG="Spool {spool_id} assigned to {tool}" +``` + +### Generating Tags from Spoolman + +If you have Spoolman running, you can generate the tag payload directly: + +```bash +SPOOL_ID=42 # Your spool ID +curl -s "http://spoolman:7912/api/v1/spool/$SPOOL_ID" | jq '{ + protocol: "openspool", + version: "1.0", + type: .filament.material, + brand: .filament.vendor.name, + color_hex: ("#" + .filament.color_hex), + spool_id: .id +}' +``` + +Write this JSON to an NTAG215 tag using any NFC app that supports NDEF with MIME type `application/json`. diff --git a/overlays/firmware-extended/13-rfid-support/examples/nfc_spoolman.cfg b/overlays/firmware-extended/13-rfid-support/examples/nfc_spoolman.cfg new file mode 100644 index 00000000..8f835db5 --- /dev/null +++ b/overlays/firmware-extended/13-rfid-support/examples/nfc_spoolman.cfg @@ -0,0 +1,25 @@ +# NFC Tag -> Spoolman Integration +# Copy this file to extended/klipper/ to enable automatic spool tracking +# +# When an NFC tag with a spool_id field is read, this macro is called +# with the channel (0-3 = T0-T3) and the spool_id from the tag. +# +# Tag format example: +# {"protocol":"openspool","version":"1.0","type":"PLA","brand":"Elegoo","color_hex":"#FF5733","spool_id":42} +# +# Customize this macro to integrate with your Spoolman setup. + +[gcode_macro ON_NFC_SPOOL_READ] +description: Called automatically when NFC tag with spool_id is read +gcode: + {% set channel = params.CHANNEL|int %} + {% set spool_id = params.SPOOL_ID|int %} + {% set tool = "T" ~ channel %} + + # Update tool's spool assignment (requires tool macros with spool_id variable) + SET_GCODE_VARIABLE MACRO={tool} VARIABLE=spool_id VALUE={spool_id} + + # Optional: Trigger deferred spool save to Moonraker/Spoolman + # UPDATE_DELAYED_GCODE ID=SAVE_SELECTED_SPOOLS DURATION=1 + + RESPOND PREFIX="NFC" MSG="Spool {spool_id} assigned to {tool}" diff --git a/overlays/firmware-extended/13-rfid-support/patches/02-add-ndef-protocol.patch b/overlays/firmware-extended/13-rfid-support/patches/02-add-ndef-protocol.patch index 8ece4593..83a71d86 100644 --- a/overlays/firmware-extended/13-rfid-support/patches/02-add-ndef-protocol.patch +++ b/overlays/firmware-extended/13-rfid-support/patches/02-add-ndef-protocol.patch @@ -7,8 +7,8 @@ diff -uNr rootfs.original/home/lava/klipper/klippy/extras/filament_detect.py roo +from . import filament_protocol_ndef from . import fm175xx_reader from . import filament_feed - -@@ -124,6 +125,14 @@ + +@@ -124,6 +125,22 @@ filament_info = info else: logging.error("channel[%d] m1 parse err: %d", channel, error) @@ -18,6 +18,15 @@ diff -uNr rootfs.original/home/lava/klipper/klippy/extras/filament_detect.py roo + if (error == filament_protocol.FILAMENT_PROTO_OK): + logging.info("channel[%d] NDEF parse ok....", channel) + filament_info = info ++ # Call macro if spool_id present in NFC tag (for Spoolman integration) ++ spool_id = info.get('SPOOL_ID') ++ if spool_id is not None: ++ try: ++ gcode = self.printer.lookup_object('gcode') ++ gcode.run_script_from_command( ++ f"ON_NFC_SPOOL_READ CHANNEL={channel} SPOOL_ID={spool_id}") ++ except Exception as e: ++ logging.warning(f"Failed to run ON_NFC_SPOOL_READ: {e}") + else: + logging.error("channel[%d] NDEF parse err....", channel) else: diff --git a/overlays/firmware-extended/13-rfid-support/root/home/lava/klipper/klippy/extras/filament_protocol_ndef.py b/overlays/firmware-extended/13-rfid-support/root/home/lava/klipper/klippy/extras/filament_protocol_ndef.py index 6de3e4ad..a7df1a9b 100644 --- a/overlays/firmware-extended/13-rfid-support/root/home/lava/klipper/klippy/extras/filament_protocol_ndef.py +++ b/overlays/firmware-extended/13-rfid-support/root/home/lava/klipper/klippy/extras/filament_protocol_ndef.py @@ -220,6 +220,9 @@ def openspool_parse_payload(payload): info['OFFICIAL'] = True info['CARD_UID'] = [] + # Extract spool_id for Spoolman integration (optional field) + info['SPOOL_ID'] = data.get('spool_id', None) + return filament_protocol.FILAMENT_PROTO_OK, info except json.JSONDecodeError as e: