-
Notifications
You must be signed in to change notification settings - Fork 6
feat: Bambu AMS tray dashboard on TFT display (#148) #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1dc3643
3288a16
b604c4a
1f61c4e
e58c253
f02c4fe
d7178d4
b35621c
3238111
3e14d9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| #include <Arduino.h> | ||
| #include <WiFi.h> | ||
| #include <HTTPClient.h> | ||
| #include <Preferences.h> | ||
| extern LEDManager ledManager; | ||
| #else | ||
| #include "platform/NativePlatform.h" | ||
|
|
@@ -60,6 +61,30 @@ bool ApplicationManager::begin(DisplayI* display) { | |
| } | ||
|
|
||
| Serial.println("ApplicationManager: Message queue created"); | ||
|
|
||
| #ifndef NATIVE_TEST | ||
| // Load cached tray dashboard from NVS | ||
| bool dashEnabled = ConfigurationManager::getInstance().isBambuDashboardEnabled(); | ||
| if (dashEnabled) { | ||
| Preferences prefs; | ||
| prefs.begin("spoolsense", true); | ||
| size_t len = prefs.getBytesLength("tray_dash"); | ||
| if (len == sizeof(TrayDashboardState)) { | ||
| prefs.getBytes("tray_dash", &trayDashboardState_, sizeof(TrayDashboardState)); | ||
| if (trayDashboardState_.has_data && display_) { | ||
| display_->showTrayDashboard(trayDashboardState_); | ||
| Serial.printf("ApplicationManager: Loaded cached tray dashboard, %d trays\n", | ||
| trayDashboardState_.tray_count); | ||
| } else if (display_) { | ||
| display_->showText("SpoolSense", "AMS Ready"); | ||
| } | ||
| } else if (display_) { | ||
| display_->showText("SpoolSense", "AMS Ready"); | ||
| } | ||
| prefs.end(); | ||
| } | ||
| #endif | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -133,6 +158,22 @@ void ApplicationManager::processMessages() { | |
| } | ||
| } | ||
|
|
||
| // Bambu dashboard revert: after scan interruption, return to dashboard | ||
| if (dashboardRevertAt_ != 0) { | ||
| uint32_t elapsedMs = static_cast<uint32_t>(millis() - dashboardRevertAt_); | ||
| if (elapsedMs >= DASHBOARD_REVERT_DELAY_MS) { | ||
| dashboardRevertAt_ = 0; | ||
| #ifndef NATIVE_TEST | ||
| bool dashEnabled = ConfigurationManager::getInstance().isBambuDashboardEnabled(); | ||
| #else | ||
| bool dashEnabled = false; | ||
| #endif | ||
| if (dashEnabled && trayDashboardState_.has_data && display_) { | ||
| display_->showTrayDashboard(trayDashboardState_); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| // ── Display ───────────────────────────────────────────────────────────────── | ||
|
|
@@ -254,6 +295,10 @@ void ApplicationManager::handleMessage(const AppMessage& msg) { | |
| case AppMessageType::KEYPAD_CANCEL: | ||
| handleKeypadCancel(); | ||
| break; | ||
|
|
||
| case AppMessageType::TRAY_UPDATE: | ||
| handleTrayUpdate(); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -404,6 +449,12 @@ void ApplicationManager::handleSpoolDetected(const AppMessage& msg) { | |
| else if (strcmp(s.tag_format, "OpenSpool") == 0) spool.tagType = 6; | ||
| else spool.tagType = 0; | ||
| display_->showSpool(spool); | ||
|
|
||
| #ifndef NATIVE_TEST | ||
| if (ConfigurationManager::getInstance().isBambuDashboardEnabled() && trayDashboardState_.has_data) { | ||
| dashboardRevertAt_ = millis(); | ||
| } | ||
| #endif | ||
| } else if (display_) { | ||
| Serial.printf("ApplicationManager: Skipping LCD update for already displayed spool %s\n", msg.payload.spoolDetected.spool_id); | ||
| } | ||
|
|
@@ -640,6 +691,12 @@ void ApplicationManager::handleBlankTagDetected(const AppMessage& msg) { | |
| lastDisplayedSpoolId[0] = '\0'; // Clear smart tag display — allow re-display if tag swapped | ||
|
|
||
| display_->showText4("**** Spool ****", "*** Scanned ***", "Unknown Tag", "Use app to setup"); | ||
|
|
||
| #ifndef NATIVE_TEST | ||
| if (ConfigurationManager::getInstance().isBambuDashboardEnabled() && trayDashboardState_.has_data) { | ||
| dashboardRevertAt_ = millis(); | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
| // HA MQTT: publish blank tag detected | ||
|
|
@@ -680,6 +737,12 @@ void ApplicationManager::handleGenericTagDetected(const AppMessage& msg) { | |
| lastDisplayedSpoolId[0] = '\0'; // Clear smart tag display — allow re-display if tag swapped | ||
|
|
||
| display_->showText4("**** Spool ****", "*** Scanned ***", "Generic Tag", "Checking Spoolman"); | ||
|
|
||
| #ifndef NATIVE_TEST | ||
| if (ConfigurationManager::getInstance().isBambuDashboardEnabled() && trayDashboardState_.has_data) { | ||
| dashboardRevertAt_ = millis(); | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
| // HA MQTT: publish generic tag (UID only, awaiting Spoolman lookup) | ||
|
|
@@ -1206,6 +1269,38 @@ void ApplicationManager::handleKeypadCancel() { | |
| } | ||
| } | ||
|
|
||
| // ── Tray Dashboard ────────────────────────────────────────────────────────── | ||
|
|
||
| void ApplicationManager::updateTrayDashboard(const TrayDashboardState& state) { | ||
| trayDashboardState_ = state; | ||
| } | ||
|
|
||
| const TrayDashboardState& ApplicationManager::getTrayDashboardState() const { | ||
| return trayDashboardState_; | ||
| } | ||
|
|
||
| void ApplicationManager::handleTrayUpdate() { | ||
| #ifndef NATIVE_TEST | ||
| // Persist to NVS | ||
| Preferences prefs; | ||
| prefs.begin("spoolsense", false); | ||
| prefs.putBytes("tray_dash", &trayDashboardState_, sizeof(TrayDashboardState)); | ||
|
Comment on lines
+1285
to
+1287
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Debounce the tray-cache writes. This writes the full dashboard blob on every 🧰 Tools🪛 Clang (14.0.6)[warning] 1284-1284: variable 'prefs' is not initialized (cppcoreguidelines-init-variables) 🤖 Prompt for AI Agents |
||
| prefs.end(); | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| #endif | ||
|
|
||
| Serial.printf("ApplicationManager: Tray dashboard updated, %d trays\n", | ||
| trayDashboardState_.tray_count); | ||
|
|
||
| #ifndef NATIVE_TEST | ||
| bool dashEnabled = ConfigurationManager::getInstance().isBambuDashboardEnabled(); | ||
| #else | ||
| bool dashEnabled = false; | ||
| #endif | ||
| if (dashEnabled && display_) { | ||
| display_->showTrayDashboard(trayDashboardState_); | ||
| } | ||
| } | ||
|
|
||
| bool ApplicationManager::sendAssignSpool(const char* toolNumber) { | ||
| // Send ASSIGN_SPOOL TOOL=Tn gcode to Moonraker (Klipper-AFC integration) | ||
| #ifndef NATIVE_TEST | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
|
|
||
| #include <cstdint> | ||
| #include "IPrinterStrategy.h" // for MAX_TOOLS constant | ||
| #include "TrayDashboardTypes.h" | ||
|
|
||
| #ifdef NATIVE_TEST | ||
| #include "platform/NativePlatform.h" | ||
|
|
@@ -30,6 +31,7 @@ enum class AppMessageType { | |
| KEYPAD_DIGIT, | ||
| KEYPAD_CONFIRM, | ||
| KEYPAD_CANCEL, | ||
| TRAY_UPDATE, | ||
| }; | ||
|
Comment on lines
+34
to
35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Carry The new flow writes Also applies to: 185-186 🤖 Prompt for AI Agents |
||
|
|
||
| enum class AutomationMode : uint8_t { | ||
|
|
@@ -180,6 +182,8 @@ class ApplicationManager { | |
| AutomationMode getAutomationMode() const { return automationMode; } | ||
| void setAutomationMode(AutomationMode mode) { automationMode = mode; } | ||
| SmartTagEnrichment getSmartTagEnrichment() const { return smartTagEnrichment_; } | ||
| void updateTrayDashboard(const TrayDashboardState& state); | ||
| const TrayDashboardState& getTrayDashboardState() const; | ||
| #ifdef NATIVE_TEST | ||
| void resetForTest() { | ||
| if (messageQueue) { vQueueDelete(messageQueue); messageQueue = nullptr; } | ||
|
|
@@ -247,6 +251,11 @@ class ApplicationManager { | |
| // Enrichment data from Spoolman UID lookup for the current smart tag | ||
| SmartTagEnrichment smartTagEnrichment_; | ||
|
|
||
| // Bambu AMS tray dashboard state | ||
| TrayDashboardState trayDashboardState_ = {}; | ||
| uint32_t dashboardRevertAt_ = 0; | ||
| static constexpr uint32_t DASHBOARD_REVERT_DELAY_MS = 5000; | ||
|
|
||
| // Handlers | ||
| void handlePrintStarted(const AppMessage& msg); | ||
| void handlePrintEnded(const AppMessage& msg); | ||
|
|
@@ -262,6 +271,7 @@ class ApplicationManager { | |
| void handleKeypadDigit(const AppMessage& msg); | ||
| void handleKeypadConfirm(); | ||
| void handleKeypadCancel(); | ||
| void handleTrayUpdate(); | ||
| bool sendAssignSpool(const char* toolNumber); | ||
| void finishPrint(float gramsUsed, bool canceled); | ||
| void enqueueSpoolmanSync(const SpoolDetectedPayload& spool); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ static const char* NVS_KEY_PRUSALINK_KEY = "prusalink_key"; | |
| static const char* NVS_KEY_NFC_READER = "nfc_reader"; | ||
| static const char* NVS_KEY_HOSTNAME = "hostname"; | ||
| static const char* NVS_KEY_LOW_SPOOL = "low_spool_g"; | ||
| static const char* NVS_KEY_BAMBU_DASH = "bambu_dash"; | ||
|
|
||
| // Sanitize hostname: enforce mDNS naming constraints (lowercase alphanum + hyphens, | ||
| // no leading/trailing hyphens) and reject empty strings to avoid boot-time errors. | ||
|
|
@@ -248,6 +249,10 @@ bool ConfigurationManager::loadFromNVS() { | |
| _lowSpoolThreshold = prefs.getUShort(NVS_KEY_LOW_SPOOL, 100); | ||
| anyOverride = true; | ||
| } | ||
| if (prefs.isKey(NVS_KEY_BAMBU_DASH)) { | ||
| _bambuDashboard = prefs.getBool(NVS_KEY_BAMBU_DASH, false); | ||
| anyOverride = true; | ||
| } | ||
|
Comment on lines
+252
to
+255
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seed This override path only runs when 🔧 Suggested fix void ConfigurationManager::loadFromDeviceConfig() {
const DeviceConfig& cfg = getDeviceConfig();
@@
_lcdEnabled = cfg.peripherals.lcd_enabled;
_ledEnabled = cfg.peripherals.status_led_enabled;
_keypadEnabled = cfg.peripherals.keypad_enabled;
+ _bambuDashboard = cfg.peripherals.bambu_dashboard;
}🤖 Prompt for AI Agents |
||
|
|
||
| prefs.end(); | ||
| return anyOverride; | ||
|
|
@@ -350,6 +355,10 @@ uint16_t ConfigurationManager::getLowSpoolThreshold() const { | |
| return _lowSpoolThreshold; | ||
| } | ||
|
|
||
| bool ConfigurationManager::isBambuDashboardEnabled() const { | ||
| return _bambuDashboard; | ||
| } | ||
|
|
||
| void ConfigurationManager::getCurrentConfig(ConfigUpdate& out) const { | ||
| memset(&out, 0, sizeof(out)); | ||
| strncpy(out.wifi_ssid, _ssid, sizeof(out.wifi_ssid) - 1); | ||
|
|
@@ -373,6 +382,7 @@ void ConfigurationManager::getCurrentConfig(ConfigUpdate& out) const { | |
| strncpy(out.nfc_reader, _nfcReader, sizeof(out.nfc_reader) - 1); | ||
| strncpy(out.hostname, _hostname, sizeof(out.hostname) - 1); | ||
| out.low_spool_threshold_g = _lowSpoolThreshold; | ||
| out.bambu_dashboard = _bambuDashboard ? 1 : 0; | ||
| } | ||
|
|
||
| #ifndef NATIVE_TEST | ||
|
|
@@ -416,6 +426,7 @@ bool ConfigurationManager::saveToNVS(const ConfigUpdate& update) { | |
| sanitizeHostname(sanitizedHostname, sizeof(sanitizedHostname)); // enforce mDNS constraints before NVS write | ||
| prefs.putString(NVS_KEY_HOSTNAME, sanitizedHostname); | ||
| prefs.putUShort(NVS_KEY_LOW_SPOOL, update.low_spool_threshold_g); | ||
| prefs.putBool(NVS_KEY_BAMBU_DASH, update.bambu_dashboard != 0); | ||
|
|
||
| // Invalidate Spoolman enrichment cache on config change to force re-fetch | ||
| // (config change could invalidate cached spool lookups) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.