Welcome to the KNOMI V2 developer documentation. This directory contains technical deep-dives, implementation details, and customization guides for developers who want to understand, modify, or extend the firmware.
| Document | Description | Topics Covered |
|---|---|---|
| UI_CUSTOMIZATION.md | Complete guide to LVGL UI customization | Layer architecture, widgets, GIF integration, color themes, performance optimization |
| PRINT_PROGRESS_FEATURE.md | Print progress system deep-dive | PSRAM management, layer compositing, Moonraker integration, tool indicators |
| HYBRID_DISPLAY.md | State machine & screen switching | Display states, state transitions, GIF management, Klipper integration |
| DISPLAY_SLEEP_IMPLEMENTATION.md | Power management system | Sleep modes, hooks, timer management, API implementation |
Start here:
- UI_CUSTOMIZATION.md - Learn the layer architecture and LVGL basics
- HYBRID_DISPLAY.md - Understand how screens switch automatically
- PRINT_PROGRESS_FEATURE.md - See how dynamic overlays work
| Goal | Read This | Key Sections |
|---|---|---|
| Add a new screen | HYBRID_DISPLAY.md | State machine, transitions |
| Customize GIFs | UI_CUSTOMIZATION.md | Filesystem GIFs, C-array GIFs |
| Modify progress display | PRINT_PROGRESS_FEATURE.md | Layer architecture, PSRAM |
| Add power management | DISPLAY_SLEEP_IMPLEMENTATION.md | Sleep hooks, timers |
| Change color themes | UI_CUSTOMIZATION.md | Tool colors, gradients |
┌─────────────────────────────────────────────────────┐
│ Moonraker API (Klipper Integration) │
│ - Printer state, temperatures, progress │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ State Machine (Hybrid Display) │
│ - Idle, Homing, Heating, Printing, Complete │
│ - Automatic screen transitions │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ LVGL UI Layer System │
│ - Background GIFs (PSRAM) │
│ - Static overlays (PNG rings) │
│ - Dynamic widgets (arcs, labels) │
└───────────────────────┬─────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Display Hardware (GC9A01) │
│ - 240x240 circular TFT │
│ - 80MHz SPI, 60fps rendering │
└─────────────────────────────────────────────────────┘
The KNOMI UI uses a 4-layer compositing system:
Layer 3: Text Labels (Foreground)
Layer 2: Progress Arc (Semi-transparent black)
Layer 1: Static Ring (Colorful PNG)
Layer 0: Animated GIF (Background from PSRAM)
Why? This allows dynamic overlays on animated backgrounds without redrawing the entire screen—critical for 60fps performance.
Learn more: UI_CUSTOMIZATION.md
The display automatically switches between modes based on printer state:
- Idle → Tool-specific GIF
- Homing → Homing animation
- Heating → Temperature sliders
- Printing → Progress overlay
- Complete → Success checkmark
No user input needed—the display adapts in real-time.
Learn more: HYBRID_DISPLAY.md
Large GIF files (100-500KB) are loaded into PSRAM (8MB external RAM) instead of internal SRAM:
Benefits:
- ✅ Keeps internal RAM free for LVGL rendering
- ✅ Fast recreation (data persists after screen switch)
- ✅ No filesystem re-reads
Learn more: PRINT_PROGRESS_FEATURE.md
Each KNOMI display detects its tool number from hostname:
knomi-t0.local → Tool 0
knomi-t1.local → Tool 1
...
knomi-t5.local → Tool 5
This enables:
- Tool-specific GIFs (e.g.,
tool_0.giffor T0) - Tool-specific colors (e.g., red for T0, green for T1)
- Synchronized sleep/wake across all 6 displays
Learn more: UI_CUSTOMIZATION.md
- PlatformIO (VSCode extension recommended)
- ESP32 toolchain (auto-installed by PlatformIO)
- SquareLine Studio (optional, for UI design)
- LVGL 8.3.7 (included in project)
# Clone repository (firmware branch)
git clone -b firmware https://github.com/PrintStructor/knomi-toolchanger.git
cd knomi-toolchanger
# Build firmware
pio run -e knomiv2
# Flash firmware
pio run -e knomiv2 -t upload
# Flash filesystem (GIFs)
pio run -e knomiv2 -t uploadfs
# Monitor serial output
pio device monitor -b 115200src/
├── ui/ # SquareLine Studio exports (auto-generated)
│ ├── screens/ # Screen definitions
│ ├── images/ # Static assets (C-arrays)
│ ├── fonts/ # Custom fonts
│ └── ui.c/h # Main UI entry point
├── ui_overlay/ # Custom logic & overlays
│ ├── lv_print_progress_update.cpp # Print progress system
│ ├── lv_moonraker_change_screen.cpp # State machine
│ └── lv_theme_color.cpp # Color themes
├── gif/ # Built-in GIF animations (C-arrays)
├── power_management/ # Display sleep system
└── main.cpp # Firmware entry point
LVGL Objects:
- Prefix with
ui_for SquareLine exports:ui_label_printing_progress - Prefix with
lv_for custom LVGL code:lv_goto_idle_screen()
Functions:
- Snake_case for C/C++ functions:
update_print_progress() - Verb-first for actions:
delete_ui_bg_gif(),show_ui_bg_ring()
Constants:
- ALL_CAPS for defines:
DISPLAY_IDLE_TIMEOUT_SEC - Prefix with
LV_for LVGL enums:LV_MOONRAKER_STATE_IDLE
Enable verbose logging:
// In main.cpp or lvgl_usr.cpp
#define DEBUG_PRINT_PROGRESS 1
#define DEBUG_STATE_MACHINE 1
#define DEBUG_PSRAM 1Monitor output:
pio device monitor -b 115200Expected logs:
[Progress] Loaded GIF: 123456 bytes into PSRAM
[Progress] Background Ring created
[Progress] Update: 42% (Layer 42/120)
[State] Transition: IDLE → PRINTING
[Sleep] WAKING FROM SLEEP (trigger: state_change)
Check FPS:
// In lvgl_usr.cpp
void loop() {
lv_task_handler();
static uint32_t last_fps_check = 0;
if (millis() - last_fps_check > 5000) { // Every 5 seconds
uint32_t fps = lv_refr_get_fps_avg();
Serial.printf("LVGL FPS: %d\n", fps);
last_fps_check = millis();
}
}Target: 55-60 FPS (at 80MHz SPI)
Check PSRAM usage:
Serial.printf("PSRAM Free: %u bytes\n", ESP.getFreePsram());
Serial.printf("PSRAM Total: %u bytes\n", ESP.getPsramSize());Check heap:
Serial.printf("Free Heap: %u bytes\n", ESP.getFreeHeap());| Library | Version | Purpose |
|---|---|---|
| LVGL | 8.3.7 | UI framework |
| Arduino-ESP32 | 2.0.x | ESP32 core |
| LittleFS | (built-in) | Filesystem for GIFs |
| WiFi | (built-in) | Network connectivity |
| HTTPClient | (built-in) | Moonraker API |
| ArduinoJson | 6.x | JSON parsing |
- README.md - Main project documentation
- FEATURES.md - Feature overview
- CHANGELOG.md - Version history
- DISPLAY_SLEEP_INSTALLATION.md - Sleep system setup
- DISPLAY_SLEEP_KLIPPER_INTEGRATION.md - Klipper macros
- FLASH_TOOL_GUIDE.md - ESP32 flashing guide
- TEMP_GRAPH_FIX.md - Temperature display fixes
- Read the code style guide above
- Review existing state machine logic in HYBRID_DISPLAY.md
- Understand the layer architecture in UI_CUSTOMIZATION.md
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Test thoroughly (all 6 displays if possible)
- Document your changes (update relevant .md files)
- Submit a pull request
- Code follows naming conventions
- No memory leaks (test with
ESP.getFreeHeap()) - FPS remains above 55 (profile with
lv_refr_get_fps_avg()) - Serial logs added for debugging
- Documentation updated
- Tested on real hardware (not just emulator)
# Use ezgif.com or ImageMagick
convert input.gif -resize 240x240 -colors 256 -coalesce -layers optimize output.gifTarget: <200KB per GIF
void test_psram() {
uint8_t* test = (uint8_t*)ps_malloc(1024 * 1024); // Allocate 1MB
if (test) {
Serial.println("PSRAM allocation OK");
free(test);
} else {
Serial.println("PSRAM allocation FAILED");
}
}void _ui_screen_change_debug(lv_obj_t ** target, ...) {
Serial.printf("[Screen] Changing from %p to %p\n", lv_scr_act(), *target);
_ui_screen_change(target, anim, time, delay, user_data);
}A: See HYBRID_DISPLAY.md - Add New State
A: Check:
- GIF file size (should be <200KB)
- Double buffering enabled in
lv_conf.h - SPI speed (should be 80MHz, not 40MHz)
- Minimize label updates (only update on change)
A: Not yet. KNOMI V2 firmware is built for LVGL 8.3.7. Porting to LVGL 9 requires significant changes.
A: Add this to setup():
Serial.printf("PSRAM: %s\n", psramFound() ? "FOUND" : "NOT FOUND");
Serial.printf("PSRAM Size: %u bytes\n", ESP.getPsramSize());
Serial.printf("PSRAM Free: %u bytes\n", ESP.getFreePsram());- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Discord: VORON Discord (#toolchangers channel)
Happy Coding! 🚀
Last Updated: December 2, 2025 Version: 1.0.0