PrintVault Pro poster: the finished enclosure with callouts for electronics design, electronics production, 3D printing, interface, inputs/outputs and artificial intelligence
Final project poster: the finished PrintVault Pro enclosure and the Fab Academy skill areas integrated into it.

Project overview

PrintVault Pro improves print reliability by stabilizing the environment inside the enclosure (closed-loop heating with material presets) and by watching the print with a camera + AI model that detects failures within seconds and notifies the user — locally, on the desktop app, and on their phone.

Working prototype Enclosure + AI

Problem

Many 3D printing materials are sensitive to drafts, ambient temperature changes, and humidity. ABS and ASA warp and crack when the chamber is cold; Nylon and Polycarbonate need even higher, stable chamber temperatures to avoid layer separation. Beyond materials, the most expensive failures happen mid-print: a part detaches, the nozzle drags filament into the air and produces the classic "spaghetti" failure. An 8-hour print can be ruined in the first 20 minutes, and unless somebody is watching, the printer keeps extruding plastic into nothing — wasting filament, time, and in the worst case becoming a safety risk.

Solution

PrintVault Pro attacks both problems at once. A heated, sensor-monitored enclosure keeps the chamber at the right temperature for each material with closed-loop hysteresis control, while a camera + YOLO model running on a Raspberry Pi 5 supervises the print in real time. When a failure is confirmed, the system raises an alert on the local OLED display, in the desktop app (pop-up), over MQTT for home-automation integration, and as a push notification to the user's phone via ntfy. Everything is controllable remotely from a PyQt6 desktop application over WiFi.

Concept sketch of the smart 3D printer enclosure showing heating resistors, ventilation fans, camera + AI, sensor, display and Raspberry Pi.
Original concept sketch: heating, ventilation fans, camera + AI monitoring, temperature/humidity sensor, display, and Raspberry Pi. The final build follows this concept closely.

Core features

  • Closed-loop chamber heating: a 100 W 12 V PTC heater driven by a MOSFET, controlled with hysteresis around a per-material setpoint (PLA 25 °C → PC 55 °C).
  • Active ventilation: a 12 V fan for cooling, fume management, and emergency heat dissipation.
  • Temperature & humidity sensing: Sensirion SHT31 over I²C, sampled every second, with a fault-tolerant watchdog.
  • Local status display: 1.3" SH1106 OLED showing live T/H, mode, setpoint, actuator states and connection status.
  • AI print monitoring: a YOLO model detects spaghetti failures on the live camera feed; alerts require 3 seconds of continuous detection to suppress false positives.
  • Multi-channel alerts: desktop pop-up, MQTT topic, and ntfy push notification to the phone.
  • Remote control: PyQt6 desktop app (packaged as a Windows .exe with PyInstaller) with live annotated video, material presets, AUTO/MANUAL modes and emergency stop.
  • Multi-layer safety: hard over-temperature cutoff, sensor watchdog, and heater shutdown on connection loss — all independent of the network.

System architecture

The system is split into three layers, each doing what it is best at. The data flow is:

Desktop App ⟷ WiFi (WebSocket + HTTP) ⟷ Raspberry Pi 5 ⟷ BLE ⟷ XIAO ESP32-S3 ⟷ sensors & actuators

  • XIAO ESP32-S3 (firmware, C++/Arduino): the real-time layer. It owns the sensors and actuators and runs the control + safety logic locally, so the enclosure remains safe even if WiFi, the Pi, or the app go down. It exposes a BLE Nordic UART Service (NUS).
  • Raspberry Pi 5 (gateway, Python/FastAPI): the intelligence layer. A single process bridges BLE ⟷ WebSocket, runs YOLO inference on the CSI camera, streams the annotated video as MJPEG, and dispatches notifications (MQTT + ntfy). It runs as a systemd service, so it starts automatically on power-up.
  • PyQt6 desktop app (Python): the user layer. It connects to the Pi over WiFi only — it never talks BLE directly — which means it can run on any computer on the network (or remotely via Tailscale).
Layer Hardware Responsibilities Transport
Real-time control XIAO ESP32-S3 + SHT31 + OLED + 3× MOSFET Sensor sampling, hysteresis control, safety cutoffs, local display BLE (Nordic UART Service)
Gateway + AI Raspberry Pi 5 + CSI camera BLE↔WS bridge, YOLO inference, MJPEG stream, MQTT/ntfy alerts, /health WiFi (HTTP + WebSocket, port 8000)
User interface Any PC (Windows .exe) Dashboard, live video, manual control, material presets, emergency stop WiFi (WebSocket client)

Why a gateway instead of direct BLE to the app? Three reasons. First, range: BLE limits the user to ~10 m from the enclosure, while WiFi (and Tailscale) allows control from anywhere. Second, the camera: the CSI camera physically connects to the Pi, so video has to flow through it anyway. Third, the AI: YOLO inference needs the Pi's CPU — the ESP32 can't run it. Centralizing everything in the Pi means the app only needs one connection to get state, video, and alerts.

Electronics & hardware design

The control electronics are built around the Seeed Studio XIAO ESP32-S3. All three loads (heater, fan, lights) are low-side switched with logic-level N-channel MOSFETs, driven directly from GPIO pins:

Signal XIAO pin Load Notes
Heater gate D0 PTC heater, 100 W @ 12 V (~8.3 A) MOSFET sized with margin; PTC is self-limiting, an extra safety property
Fan gate D9 12 V fan Flyback diode across the motor (inductive load)
Lights gate D8 12 V LED strip Chamber illumination — also needed by the camera/AI at night
I²C SDA / SCL D4 / D5 SHT31 (0x44) + SH1106 OLED (0x3C) Both devices share the same I²C bus at different addresses

The choice of a PTC heater (instead of resistance wire or heater cartridges) is deliberate: PTC elements increase their resistance as they heat up, so their power output self-limits — even in a worst-case electronics failure they cannot reach ignition temperatures the way nichrome wire can. The SHT31 was chosen over the common DHT11/DHT22 because it is a true I²C device with ±0.2 °C accuracy and much better long-term reliability — and accuracy matters when the control hysteresis band is only ±2 °C.

Firmware (XIAO ESP32-S3)

The firmware is a single non-blocking loop (no delay()-based logic) that every cycle: reads the sensor (1 Hz), runs the safety + control logic, applies outputs, and publishes the state over BLE (1 Hz) while refreshing the OLED. Communication uses the Nordic UART Service (NUS) profile: a Write characteristic (RX) receives newline-terminated text commands, and a Notify characteristic (TX) pushes one JSON state line per second:

{"t":32.4,"h":38,"heater":1,"fan":0,"lights":1,
 "mode":"AUTO","sp":45,"status":"NORMAL"}
Command Action
H:1 / H:0Heater on/off (only honored in MANUAL mode — in AUTO the control loop owns the heater)
F:1 / F:0Fan on/off
L:1 / L:0Lights on/off
MODE:AUTO / MODE:MANUALSwitch between closed-loop and manual control
SP:45Setpoint in °C, clamped in firmware to 20–60 °C
MAT:ABSMaterial selection (informative, shown on display)
EMERGENCYHeater off, fan forced on, AUTO disabled, status ALERT

In AUTO mode the heater is governed by hysteresis control: it turns on below setpoint − 2 °C and off above setpoint + 2 °C; inside the band it keeps its previous state. This avoids the rapid on/off relay-chatter a simple threshold would cause, which matters for MOSFET/heater longevity. A PID loop was considered, but a thermal chamber is a slow, heavily damped system — hysteresis reaches the same steady state with one tunable parameter instead of three.

Safety design

Safety logic runs every loop iteration, before anything else, and independently of connectivity. The layers, in order of evaluation:

  • Sensor watchdog: the SHT31 occasionally returns NaN glitches. A single bad reading does not kill the heater — the firmware tolerates up to 3 consecutive failures while holding the last valid value. But if failures persist, or no valid reading arrives for 5 seconds, the sensor is declared dead and the heater is forced off. Rationale: a heater running blind is the single most dangerous state of the system.
  • Hard over-temperature cutoff: at ≥ 65 °C the heater is cut and the fan is forced on, regardless of mode, setpoint, or any command. The user-settable setpoint is capped at 60 °C, leaving a 5 °C guard band below the hard limit.
  • Early warning: within 5 °C of the hard limit, the reported status changes to WARN so the app and OLED show it before a cutoff happens.
  • Connection-loss policy: if BLE drops while in MANUAL mode, the heater is switched off — a heater left on manually must never keep running unsupervised. AUTO mode survives a disconnection because the closed loop keeps it safe.
  • Safe boot: all output pins are driven LOW before anything else in setup(), so a brown-out reboot can never leave a load energized by accident.

Raspberry Pi gateway

The gateway (pi_gateway.py) merges what used to be separate scripts into one FastAPI process, for a hardware reason: the CSI camera can only be opened by one process at a time, and both the AI detector and the video stream need its frames. Running them in one process means the MJPEG stream serves the exact frame YOLO annotated — the user sees precisely what the AI sees, bounding boxes included.

Endpoint Type Purpose
/wsWebSocketPushes XIAO state + AI alerts to the app; receives commands from the app and forwards them over BLE
/videoHTTP (MJPEG)Live camera stream, annotated by YOLO with detections and status text
/healthHTTP (JSON)Liveness: BLE connection state, AI status, number of connected clients, age of last state

Internally the process runs three concurrent units: the BLE bridge (asyncio task using Bleak, with automatic scan-and-reconnect every 5 s if the XIAO disappears), the detector (a dedicated OS thread, because camera capture and YOLO inference are blocking operations that would freeze the asyncio event loop), and the FastAPI/uvicorn server. The thread hands frames and alerts back to the async world with asyncio.run_coroutine_threadsafe, and the MJPEG generator blocks on a condition variable so it serves frames at exactly the rate the detector produces them — no busy-waiting, no duplicated frames.

The gateway is deployed as a systemd service (fab-gateway.service) with Restart=always: plugging in the Raspberry Pi is enough to bring the whole gateway online in under a minute, and a crash in any component (camera glitch, BLE stack) gets the process restarted automatically. WorkingDirectory is set so the YOLO weights (best.pt) resolve correctly, and the unit orders itself after bluetooth.target so BlueZ is ready before the first scan.

AI print monitoring

Failure detection uses a YOLO object-detection model trained to recognize the "spaghetti" failure pattern, running entirely on the Raspberry Pi 5 (edge inference, no cloud). The inference pipeline, tuned for the Pi's CPU:

  • Capture: Picamera2 at 640×480 RGB.
  • Preprocessing: frames are converted to grayscale and expanded back to 3 channels. This makes detection robust to filament color and chamber-light color — spaghetti is recognized by shape and texture, not color — and matches how the model was trained.
  • Inference: 320 px input size with confidence threshold 0.25. The reduced input size keeps inference fast enough for continuous monitoring on the Pi; spaghetti is a large, distinctive pattern, so small-object resolution is not needed.
  • Temporal confirmation: a detection must persist for 3 continuous seconds before an alert fires. Single-frame false positives (a hand passing by, a lighting flicker, a glare) never trigger anything.
  • Cooldown: after an alert, external notifications are suppressed for 60 seconds so one failure produces one notification, not a phone-buzzing flood.

When a failure is confirmed, the alert fans out over three channels simultaneously: a WebSocket message the desktop app turns into a pop-up and a red status, an MQTT publish on impresora/errores/spaghetti (ready for Home Assistant or any other automation), and an ntfy push notification that reaches the user's phone even when they are away from the computer.

Desktop application

The control app is built with PyQt6. The UI layout was designed in Qt Designer and compiled with pyuic6 (frontendFAB.py); the logic lives in backendFAB.py. Because PyQt's event loop and Python's asyncio loop don't normally coexist, the app uses qasync to merge them — the WebSocket client and the health monitor run as asyncio tasks inside the Qt application without blocking the UI.

  • WebSocket client: receives the 1 Hz state JSON and the AI messages, and queues outgoing commands. It reconnects automatically every 3 s if the link drops.
  • MJPEG viewer: a QThread reads the raw HTTP stream, finds JPEG frame boundaries (FFD8…FFD9 markers) in the byte stream, and emits each frame as a QImage scaled into the camera panel.
  • Health monitor: polls /health every 5 s, so the sidebar distinguishes three situations: Pi offline, Pi online but XIAO unreachable over BLE, and everything connected.
  • Material presets: selecting a material (PLA, PETG, ABS, ASA, TPU, Nylon, PC) automatically sends the right chamber setpoint; "Custom" unlocks the spinbox for free adjustment.
  • State reconciliation: the UI never trusts itself — every indicator is corrected by the state the XIAO actually reports, so the app always reflects physical reality, not the last button pressed.
  • Distribution: the app is packaged with PyInstaller (--onefile --windowed) into a standalone Windows executable, so it runs on any PC without a Python installation.

How it was made — Fab Academy processes

PrintVault Pro was built almost entirely with the digital-fabrication and electronics processes learned during the Fab Academy weekly assignments. Every major subsystem of the enclosure maps to a skill area from the course:

Sheet metal chassis (cutting and bending)

The main body of the enclosure is made of sheet metal. The panels were cut to size with an angle grinder and then bent on a sheet-metal brake to form the structural chassis. Metal was chosen over wood or acrylic for the body because the chamber runs at up to 55–60 °C with a 100 W heater inside: metal is non-flammable, dimensionally stable at those temperatures, and acts as a thermal mass that helps the hysteresis control hold a steady temperature. Bent flanges (instead of bolted corner brackets) give the box rigidity with fewer parts and fewer air leaks.

Laser cutting (computer-controlled cutting)

The doors are laser-cut acrylic. Acrylic was selected for the doors for two reasons: the user needs to see the print without opening the chamber (every door opening drops the chamber temperature and risks warping), and the camera/AI benefit from a closed, draft-free environment. The door outlines, hinge mounting holes and handle holes were drawn in CAD and cut on the laser, so the holes line up exactly with the 3D-printed hinges and handles with no manual drilling.

3D printing (additive manufacturing)

All the custom mechanical hardware was 3D printed in ABS:

  • Enclosure for the custom PCBs — protects the electronics and keeps wiring organized in the electronics compartment.
  • Case for the Raspberry Pi 5 — with ventilation, since the Pi runs continuous YOLO inference and needs airflow.
  • Door handles — printed to fit the laser-cut holes in the acrylic.
  • Hinges — custom printed hinges joining the acrylic doors to the metal chassis.

ABS was a deliberate material choice, not a default: with a glass transition around 105 °C, it withstands the chamber's operating temperature with a wide margin, while PLA (Tg ≈ 60 °C) would soften and creep on parts mounted near or inside a 55 °C chamber. Fittingly, printing reliable ABS parts is exactly the kind of job the finished enclosure makes easier — the project's own parts justify the project.

Electronics design (custom PCB)

Instead of leaving the prototype on perfboard, a custom PCB was designed in KiCad to integrate the system: the XIAO ESP32-S3 footprint, the three MOSFET driver stages (heater, fan, lights) with gate resistors and flyback protection, screw-terminal connectors for the 12 V loads, and headers for the I²C devices (SHT31 sensor and SH1106 OLED). Designing the board meant applying the course material on schematic capture, footprint assignment, and routing — with trace widths on the heater path sized for ~8.3 A.

Electronics production

The boards were then fabricated and assembled in-house: producing the PCB, soldering the MOSFETs, terminals and headers, and validating each driver stage with a multimeter and a dummy load before connecting the real heater. The board lives in its 3D-printed ABS case inside the electronics compartment, separated from the heated volume.

Embedded programming, inputs and outputs

The XIAO ESP32-S3 firmware (C++/Arduino) brings together the input- and output-devices weeks: inputs are the SHT31 temperature/humidity sensor over I²C and the CSI camera on the Pi; outputs are the PTC heater, the fan and the LED lights through the MOSFET stages, plus the SH1106 OLED as a local display. The firmware implements the closed-loop hysteresis control and the multi-layer safety logic described below.

Networking and communications

The system uses two link layers in series — BLE (Nordic UART Service) between the XIAO and the Raspberry Pi, and WiFi (WebSocket + HTTP/MJPEG + MQTT) between the Pi and the desktop app — with a small text/JSON application protocol shared end-to-end.

Interface and application programming

The user interface is the PyQt6 desktop application: the layout designed in Qt Designer, the logic in Python, and the whole thing packaged into a standalone Windows executable with PyInstaller. The dashboard shows live temperature/humidity, actuator states, the annotated camera feed, and material presets.

Artificial intelligence

The final skill layer is the YOLO-based spaghetti detection running at the edge on the Raspberry Pi 5 — covered in depth in the AI print monitoring section.

Mechanical design

The enclosure is designed around airflow, thermal stability, and serviceability:

  • Bent sheet-metal chassis: non-flammable, rigid, and thermally stable at chamber temperature; flanged bends seal the box better than bolted corners.
  • Laser-cut acrylic doors with 3D-printed ABS hinges and handles, so the print is visible without opening the chamber and losing heat.
  • The PTC heater is mounted away from printed/plastic parts, with metal standoffs and a guard, so radiant heat cannot reach anything meltable.
  • The fan is positioned for exhaust so it can both regulate temperature in normal operation and dump heat fast during an emergency stop.
  • The camera is mounted with a clear, fixed view of the build plate — a stable framing is important for the AI, since the model sees a consistent background.
  • LED lighting is placed to illuminate the print evenly for the camera, avoiding hard shadows that look like spaghetti to the detector.
  • Electronics (custom PCB in its ABS case, Raspberry Pi 5 in its ventilated ABS case, power conversion) live in a separate compartment outside the heated volume — the chamber can reach 55–60 °C, which is hostile to electronics, and the Pi needs cool air for sustained YOLO inference.

Testing & validation

  • Thermal loop: verified that the chamber reaches and holds each material setpoint within the ±2 °C hysteresis band, and that the 65 °C hard cutoff fires with the heater forced on manually.
  • Sensor watchdog: validated by disconnecting the SHT31 mid-operation — heater cut off within the 5 s timeout; serial diagnostics log every NaN reading and every heater state transition with the reason.
  • AI detection: validated with provoked spaghetti failures; the 3-second confirmation eliminated false alerts from hands and lighting changes during normal use.
  • Recovery: power-cycled the Pi (systemd brings the gateway back automatically), rebooted the XIAO (BLE bridge re-scans and reconnects), and killed the app (WebSocket reconnects on relaunch) — the system recovers from each failure without manual intervention.

Next steps

  • Expand the AI model with more failure classes (detachment, no extrusion / dry runs).
  • Wire the PAUSE command to the printer (octoprint/Klipper API or a relay on the printer's pause input) so a confirmed failure can stop the print automatically. (Direct integration is not possible on Bambu printers.)
  • Move the app's gateway IP from a hardcoded constant to a config.json / settings dialog, so the .exe works on any network without recompiling.
  • Remote access through Tailscale for monitoring prints away from home.
  • Custom PCB (KiCad) integrating the XIAO, MOSFET drivers and connectors, replacing the prototype wiring.

Bill of Materials

Components used in the working prototype.

BOM Prototype
Qty Item Role Notes
1 Raspberry Pi 5 Gateway + AI inference Runs FastAPI gateway as a systemd service; YOLO at the edge, no cloud
1 Seeed Studio XIAO ESP32-S3 Real-time controller BLE NUS server; sensor sampling, hysteresis control, safety logic
1 Raspberry Pi CSI camera module Print monitoring 640×480 capture via Picamera2; fixed mount facing the build plate
1 Sensirion SHT31 Temperature + humidity I²C @ 0x44, ±0.2 °C; chosen over DHT for accuracy and reliability
1 SH1106 OLED 1.3" 128×64 Local status display I²C @ 0x3C, shares bus with the SHT31
1 PTC heater 100 W, 12 V Chamber heating Self-limiting element — inherently safer than resistance wire
1 12 V fan Ventilation / emergency cooling Forced on automatically on over-temperature and emergency stop
1 12 V LED strip Chamber lighting Even illumination for the camera; switched by MOSFET
3 Logic-level N-channel MOSFETs Load switching Low-side drivers for heater (~8.3 A), fan and lights
1 Custom PCB (KiCad design) System integration XIAO + 3× MOSFET driver stages + screw terminals + I²C headers; in-house production
Sheet metal panels Enclosure chassis Cut with angle grinder, bent on a sheet-metal brake
2 Acrylic doors Visibility + thermal seal Laser cut, with holes for 3D-printed hinges and handles
3D-printed parts (ABS) PCB case, Pi 5 case, hinges, handles ABS for heat resistance (Tg ≈ 105 °C vs chamber up to 60 °C)
1 12 V power supply (≥ 120 W) Main power Sized for heater + fan + lights with margin; Pi powered separately (5 V/5 A USB-C)

Presentation video