From d70b187bf9303a2fba3bac9be6bf3ab14d5be339 Mon Sep 17 00:00:00 2001 From: Manuel Weiser Date: Mon, 24 Feb 2025 19:14:51 +0100 Subject: [PATCH] feat: implement auto send feature for Bambu spool management and update related configurations --- html/spoolman.html | 9 +++- src/api.cpp | 111 +++++++++++++++------------------------------ src/api.h | 3 +- src/bambu.cpp | 48 +++++++++++++++++--- src/bambu.h | 4 +- src/config.cpp | 4 ++ src/config.h | 2 + src/main.cpp | 23 +++++++++- src/website.cpp | 18 +++----- 9 files changed, 124 insertions(+), 98 deletions(-) diff --git a/html/spoolman.html b/html/spoolman.html index fd3420c..ddcb430 100644 --- a/html/spoolman.html +++ b/html/spoolman.html @@ -74,8 +74,9 @@ const ip = document.getElementById('bambuIp').value; const serial = document.getElementById('bambuSerial').value; const code = document.getElementById('bambuCode').value; + const autoSend = document.getElementById('autoSend').checked; - fetch(`/api/bambu?bambu_ip=${encodeURIComponent(ip)}&bambu_serialnr=${encodeURIComponent(serial)}&bambu_accesscode=${encodeURIComponent(code)}`) + fetch(`/api/bambu?bambu_ip=${encodeURIComponent(ip)}&bambu_serialnr=${encodeURIComponent(serial)}&bambu_accesscode=${encodeURIComponent(code)}&autoSend=${autoSend}`) .then(response => response.json()) .then(data => { if (data.healthy) { @@ -121,6 +122,12 @@ +
+ If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool. + + +
+

diff --git a/src/api.cpp b/src/api.cpp index e3b13ef..5289f32 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -37,9 +37,9 @@ struct SendToApiParams { } */ -JsonDocument fetchSpoolsForWebsite() { +JsonDocument fetchSingleSpoolInfo(int spoolId) { HTTPClient http; - String spoolsUrl = spoolmanUrl + apiUrl + "/spool"; + String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId; Serial.print("Rufe Spool-Daten von: "); Serial.println(spoolsUrl); @@ -56,84 +56,45 @@ JsonDocument fetchSpoolsForWebsite() { Serial.print("Fehler beim Parsen der JSON-Antwort: "); Serial.println(error.c_str()); } else { - JsonArray spools = doc.as(); - JsonArray filteredSpools = filteredDoc.to(); + String filamentType = doc["filament"]["material"].as(); + String filamentBrand = doc["filament"]["vendor"]["name"].as(); - for (JsonObject spool : spools) { - JsonObject filteredSpool = filteredSpools.add(); - filteredSpool["extra"]["nfc_id"] = spool["extra"]["nfc_id"]; + int nozzle_temp_min = 0; + int nozzle_temp_max = 0; + if (doc["filament"]["extra"]["nozzle_temperature"].is()) { + String tempString = doc["filament"]["extra"]["nozzle_temperature"].as(); + tempString.replace("[", ""); + tempString.replace("]", ""); + int commaIndex = tempString.indexOf(','); + + if (commaIndex != -1) { + nozzle_temp_min = tempString.substring(0, commaIndex).toInt(); + nozzle_temp_max = tempString.substring(commaIndex + 1).toInt(); + } + } - JsonObject filament = filteredSpool["filament"].to(); - filament["sm_id"] = spool["id"]; - filament["id"] = spool["filament"]["id"]; - filament["name"] = spool["filament"]["name"]; - filament["material"] = spool["filament"]["material"]; - filament["color_hex"] = spool["filament"]["color_hex"]; - filament["nozzle_temperature"] = spool["filament"]["extra"]["nozzle_temperature"]; // [190,230] - filament["price_meter"] = spool["filament"]["extra"]["price_meter"]; - filament["price_gramm"] = spool["filament"]["extra"]["price_gramm"]; + String filamentColor = doc["filament"]["color_hex"].as(); + filamentColor.toUpperCase(); - JsonObject vendor = filament["vendor"].to(); - vendor["id"] = spool["filament"]["vendor"]["id"]; - vendor["name"] = spool["filament"]["vendor"]["name"]; - } - } - } else { - Serial.print("Fehler beim Abrufen der Spool-Daten. HTTP-Code: "); - Serial.println(httpCode); - } + String tray_info_idx = doc["filament"]["extra"]["bambu_idx"].as(); + tray_info_idx.replace("\"", ""); + + String cali_idx = doc["filament"]["extra"]["bambu_cali_id"].as(); // "\"153\"" + cali_idx.replace("\"", ""); + + String bambu_setting_id = doc["filament"]["extra"]["bambu_setting_id"].as(); // "\"PFUSf40e9953b40d3d\"" + bambu_setting_id.replace("\"", ""); - http.end(); - return filteredDoc; -} + doc.clear(); -JsonDocument fetchAllSpoolsInfo() { - HTTPClient http; - String spoolsUrl = spoolmanUrl + apiUrl + "/spool"; - - Serial.print("Rufe Spool-Daten von: "); - Serial.println(spoolsUrl); - - http.begin(spoolsUrl); - int httpCode = http.GET(); - - JsonDocument filteredDoc; - if (httpCode == HTTP_CODE_OK) { - String payload = http.getString(); - JsonDocument doc; - DeserializationError error = deserializeJson(doc, payload); - if (error) { - Serial.print("Fehler beim Parsen der JSON-Antwort: "); - Serial.println(error.c_str()); - } else { - JsonArray spools = doc.as(); - JsonArray filteredSpools = filteredDoc.to(); - - for (JsonObject spool : spools) { - JsonObject filteredSpool = filteredSpools.add(); - filteredSpool["price"] = spool["price"]; - filteredSpool["remaining_weight"] = spool["remaining_weight"]; - filteredSpool["used_weight"] = spool["used_weight"]; - filteredSpool["extra"]["nfc_id"] = spool["extra"]["nfc_id"]; - - JsonObject filament = filteredSpool["filament"].to(); - filament["id"] = spool["filament"]["id"]; - filament["name"] = spool["filament"]["name"]; - filament["material"] = spool["filament"]["material"]; - filament["density"] = spool["filament"]["density"]; - filament["diameter"] = spool["filament"]["diameter"]; - filament["spool_weight"] = spool["filament"]["spool_weight"]; - filament["color_hex"] = spool["filament"]["color_hex"]; - - JsonObject vendor = filament["vendor"].to(); - vendor["id"] = spool["filament"]["vendor"]["id"]; - vendor["name"] = spool["filament"]["vendor"]["name"]; - - JsonObject extra = filament["extra"].to(); - extra["nozzle_temperature"] = spool["filament"]["extra"]["nozzle_temperature"]; - extra["price_gramm"] = spool["filament"]["extra"]["price_gramm"]; - extra["price_meter"] = spool["filament"]["extra"]["price_meter"]; - } + filteredDoc["color"] = filamentColor; + filteredDoc["type"] = filamentType; + filteredDoc["nozzle_temp_min"] = nozzle_temp_min; + filteredDoc["nozzle_temp_max"] = nozzle_temp_max; + filteredDoc["brand"] = filamentBrand; + filteredDoc["tray_info_idx"] = tray_info_idx; + filteredDoc["cali_idx"] = cali_idx; + filteredDoc["bambu_setting_id"] = bambu_setting_id; } } else { Serial.print("Fehler beim Abrufen der Spool-Daten. HTTP-Code: "); diff --git a/src/api.h b/src/api.h index 3b80c93..7a5a5cb 100644 --- a/src/api.h +++ b/src/api.h @@ -14,8 +14,7 @@ bool checkSpoolmanInstance(const String& url); bool saveSpoolmanUrl(const String& url); String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder -JsonDocument fetchSpoolsForWebsite(); // API-Funktion für die Webseite -JsonDocument fetchAllSpoolsInfo(); +JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite void sendAmsData(AsyncWebSocketClient *client); // Neue Funktion zum Senden von AMS-Daten bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion zum Aktualisieren eines Spools uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts diff --git a/src/bambu.cpp b/src/bambu.cpp index e86f63e..8736f30 100644 --- a/src/bambu.cpp +++ b/src/bambu.cpp @@ -24,13 +24,15 @@ const char* bambu_ip = nullptr; const char* bambu_accesscode = nullptr; const char* bambu_serialnr = nullptr; bool bambu_connected = false; +bool autoSendToBambu = false; +int autoSetToBambuSpoolId = 0; // Globale Variablen für AMS-Daten int ams_count = 0; String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients -AMSData ams_data[MAX_AMS]; // Definition des Arrays +AMSData ams_data[MAX_AMS]; // Definition des Arrays; -bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode) { +bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode, bool autoSend) { if (BambuMqttTask) { vTaskDelete(BambuMqttTask); } @@ -39,6 +41,7 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String doc["bambu_ip"] = ip; doc["bambu_accesscode"] = accesscode; doc["bambu_serialnr"] = serialnr; + doc["autoSendToBambu"] = autoSend; if (!saveJsonValue("/bambu_credentials.json", doc)) { Serial.println("Fehler beim Speichern der Bambu-Credentials."); @@ -49,6 +52,7 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String bambu_ip = ip.c_str(); bambu_accesscode = accesscode.c_str(); bambu_serialnr = serialnr.c_str(); + autoSendToBambu = autoSend; vTaskDelay(100 / portTICK_PERIOD_MS); if (!setupMqtt()) return false; @@ -63,6 +67,7 @@ bool loadBambuCredentials() { String ip = doc["bambu_ip"].as(); String code = doc["bambu_accesscode"].as(); String serial = doc["bambu_serialnr"].as(); + autoSendToBambu = doc["autoSendToBambu"].as(); ip.trim(); code.trim(); @@ -256,6 +261,26 @@ bool setBambuSpool(String payload) { return true; } +void autoSetSpool(int spoolId, uint8_t trayId) { + // wenn neue spule erkannt und autoSetToBambu > 0 + JsonDocument spoolInfo = fetchSingleSpoolInfo(spoolId); + + if (!spoolInfo.isNull()) + { + // AMS und TRAY id ergänzen + spoolInfo["amsId"] = 0; + spoolInfo["trayId"] = trayId; + + Serial.println("Auto set spool"); + Serial.println(spoolInfo.as()); + + setBambuSpool(spoolInfo.as()); + } + + // id wieder zurücksetzen damit abgeschlossen + autoSetToBambuSpoolId = 0; +} + // init void mqtt_callback(char* topic, byte* payload, unsigned int length) { String message; @@ -267,16 +292,27 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) { // JSON-Dokument parsen JsonDocument doc; DeserializationError error = deserializeJson(doc, message); - if (error) { + if (error) + { Serial.print("Fehler beim Parsen des JSON: "); Serial.println(error.c_str()); return; } + // Wenn bambu auto set spool aktiv und eine spule erkannt und mqtt meldung das neue spule im ams + if (autoSendToBambu && autoSetToBambuSpoolId > 0 && + doc["print"]["command"].as() == "push_status" && doc["print"]["ams"]["tray_pre"].as() + && !doc["print"]["ams"]["ams"].as()) + { + autoSetSpool(autoSetToBambuSpoolId, doc["print"]["ams"]["tray_pre"].as()); + } + // Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren - if (doc["print"]["upgrade_state"].is()) { + if (doc["print"]["upgrade_state"].is()) + { // Prüfen ob AMS-Daten vorhanden sind - if (!doc["print"]["ams"].is() || !doc["print"]["ams"]["ams"].is()) { + if (!doc["print"]["ams"].is() || !doc["print"]["ams"]["ams"].is()) + { return; } @@ -470,7 +506,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) { void reconnect() { // Loop until we're reconnected while (!client.connected()) { - Serial.print("Attempting MQTT connection..."); + Serial.println("Attempting MQTT connection..."); bambu_connected = false; oledShowTopRow(); diff --git a/src/bambu.h b/src/bambu.h index 6584bff..61f68fa 100644 --- a/src/bambu.h +++ b/src/bambu.h @@ -28,9 +28,11 @@ extern bool bambu_connected; extern int ams_count; extern AMSData ams_data[MAX_AMS]; +extern bool autoSendToBambu; +extern int autoSetToBambuSpoolId; bool loadBambuCredentials(); -bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode); +bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode, const bool autoSend); bool setupMqtt(); void mqtt_loop(void * parameter); bool setBambuSpool(String payload); diff --git a/src/config.cpp b/src/config.cpp index 6b50d42..8c6e814 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -40,6 +40,10 @@ const uint8_t webserverPort = 80; const char* apiUrl = "/api/v1"; // ***** API +// ***** Bambu Auto Set Spool +uint8_t autoSetBambuAmsCounter = 60; +// ***** Bambu Auto Set Spool + // ***** Task Prios uint8_t rfidTaskCore = 1; uint8_t rfidTaskPrio = 1; diff --git a/src/config.h b/src/config.h index 8cb867e..822ff8d 100644 --- a/src/config.h +++ b/src/config.h @@ -23,6 +23,8 @@ extern const uint8_t OLED_DATA_END; extern const char* apiUrl; extern const uint8_t webserverPort; +extern uint8_t autoSetBambuAmsCounter; + extern const unsigned char wifi_on[]; extern const unsigned char wifi_off[]; extern const unsigned char cloud_on[]; diff --git a/src/main.cpp b/src/main.cpp index d089ad6..57841f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -90,6 +90,10 @@ void setup() { unsigned long lastWeightReadTime = 0; const unsigned long weightReadInterval = 1000; // 1 second +unsigned long lastAutoSetBambuAmsTime = 0; +const unsigned long autoSetBambuAmsInterval = 1000; // 1 second +uint8_t autoAmsCounter = 0; + unsigned long lastAmsSendTime = 0; const unsigned long amsSendInterval = 60000; // 1 minute @@ -108,6 +112,22 @@ void loop() { sendAmsData(nullptr); } + // Wenn Bambu auto set Spool aktiv + if (autoSendToBambu && autoSetToBambuSpoolId > 0 && currentMillis - lastAutoSetBambuAmsTime >= autoSetBambuAmsInterval) + { + lastAutoSetBambuAmsTime = currentMillis; + oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s"); + autoAmsCounter++; + + if (autoAmsCounter >= autoSetBambuAmsCounter) + { + autoSetToBambuSpoolId = 0; + autoAmsCounter = 0; + oledShowWeight(weight); + } + } + + // Wenn Waage nicht Kalibriert if (scaleCalibrated == 3) { @@ -120,7 +140,7 @@ void loop() { } // Ausgabe der Waage auf Display - if (pauseMainTask == 0 && weight != lastWeight && hasReadRfidTag == 0) + if (pauseMainTask == 0 && weight != lastWeight && hasReadRfidTag == 0 && (!autoSendToBambu || autoSetToBambuSpoolId == 0)) { (weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight); } @@ -169,6 +189,7 @@ void loop() { oledShowIcon("success"); vTaskDelay(2000 / portTICK_PERIOD_MS); weightSend = 1; + autoSetToBambuSpoolId = spoolId.toInt(); } else { diff --git a/src/website.cpp b/src/website.cpp index 9fd9d64..8e5d221 100644 --- a/src/website.cpp +++ b/src/website.cpp @@ -351,17 +351,6 @@ void setupWebserver(AsyncWebServer &server) { Serial.println("RFID-Seite gesendet"); }); - /* - // Neue API-Route für das Abrufen der Spool-Daten - server.on("/api/spools", HTTP_GET, [](AsyncWebServerRequest *request){ - Serial.println("API-Aufruf: /api/spools"); - JsonDocument spoolsData = fetchSpoolsForWebsite(); - String response; - serializeJson(spoolsData, response); - request->send(200, "application/json", response); - }); - */ - server.on("/api/url", HTTP_GET, [](AsyncWebServerRequest *request){ Serial.println("API-Aufruf: /api/url"); String jsonResponse = "{\"spoolman_url\": \"" + String(spoolmanUrl) + "\"}"; @@ -389,6 +378,7 @@ void setupWebserver(AsyncWebServer &server) { String bambuIp = doc["bambu_ip"].as(); String bambuSerial = doc["bambu_serialnr"].as(); String bambuCode = doc["bambu_accesscode"].as(); + autoSendToBambu = doc["autoSendToBambu"].as(); bambuIp.trim(); bambuSerial.trim(); bambuCode.trim(); @@ -396,12 +386,14 @@ void setupWebserver(AsyncWebServer &server) { html.replace("{{bambuIp}}", bambuIp ? bambuIp : ""); html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : ""); html.replace("{{bambuCode}}", bambuCode ? bambuCode : ""); + html.replace("{{autoSendToBambu}}", autoSendToBambu ? "checked" : ""); } else { html.replace("{{bambuIp}}", ""); html.replace("{{bambuSerial}}", ""); html.replace("{{bambuCode}}", ""); + html.replace("{{autoSendToBambu}}", ""); } request->send(200, "text/html", html); @@ -433,6 +425,8 @@ void setupWebserver(AsyncWebServer &server) { String bambu_ip = request->getParam("bambu_ip")->value(); String bambu_serialnr = request->getParam("bambu_serialnr")->value(); String bambu_accesscode = request->getParam("bambu_accesscode")->value(); + bool autoSend = (request->getParam("autoSend")->value() == "true") ? true : false; + Serial.println(autoSend); bambu_ip.trim(); bambu_serialnr.trim(); bambu_accesscode.trim(); @@ -442,7 +436,7 @@ void setupWebserver(AsyncWebServer &server) { return; } - bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode); + bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode, autoSend); request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}"); });