Compare commits

..

No commits in common. "main" and "v1.3.99" have entirely different histories.

19 changed files with 81 additions and 15563 deletions

View File

@ -1,21 +1,5 @@
# Changelog # Changelog
## [1.4.0] - 2025-03-01
### Added
- add support for Spoolman Octoprint Plugin in README files
- add OctoPrint integration with configurable fields and update functionality
- add version comparison function and check for outdated versions before updates
- remove unused version and protocol fields from JSON output; add error message for insufficient memory
### Changed
- update NFC tag references to include NTAG213 and clarify storage capacity
- bump version to 1.4.0
- remove unused version and protocol fields from NFC data packet
- sort vendors alphabetically in the dropdown list
- Merge pull request #10 from janecker/nfc-improvements
- Improves NFC Tag handling
## [1.3.99] - 2025-02-28 ## [1.3.99] - 2025-02-28
### Changed ### Changed
- update platformio.ini for version v1.3.99 - update platformio.ini for version v1.3.99

View File

@ -8,8 +8,7 @@ Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/
Weitere Bilder finden Sie im [img Ordner](/img/) Weitere Bilder finden Sie im [img Ordner](/img/)
oder auf meiner Website: [FilaMan Website](https://www.filaman.app) oder auf meiner Website: [FilaMan Website](https://www.filaman.app)
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU) Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki) ### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
@ -19,7 +18,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman). - **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman).
- **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration. - **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration.
- **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung. - **MQTT-Integration:** Verbindet sich mit Bambu Lab Drucker für AMS-Steuerung.
- **NFC-Tag NTAG213 NTAG215:** Verwendung von NTAG213, besser NTAG215 wegen ausreichendem Speicherplatz auf dem Tag - **NFC-Tag NTAG215:** Verwendung von NTAG215 wegen ausreichendem Speicherplatz auf dem Tag
### Weboberflächen-Funktionen ### Weboberflächen-Funktionen
- **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates. - **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates.
@ -36,7 +35,6 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- Filtern und Auswählen von Filamenten. - Filtern und Auswählen von Filamenten.
- Automatische Aktualisierung der Spulengewichte. - Automatische Aktualisierung der Spulengewichte.
- Verfolgung von NFC-Tag-Zuweisungen. - Verfolgung von NFC-Tag-Zuweisungen.
- Unterstützt das Spoolman Octoprint Plugin
### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee ### Wenn Sie meine Arbeit unterstützen möchten, freue ich mich über einen Kaffee
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
@ -63,7 +61,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
[Amazon Link](https://amzn.eu/d/0AuBp2c) [Amazon Link](https://amzn.eu/d/0AuBp2c)
- **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen. - **PN532 NFC NXP RFID-Modul V3:** Für NFC-Tag-Operationen.
[Amazon Link](https://amzn.eu/d/jfIuQXb) [Amazon Link](https://amzn.eu/d/jfIuQXb)
- **NFC Tags NTAG213 NTA215:** RFID Tag - **NFC Tags Ntag215:** RFID Tag
[Amazon Link](https://amzn.eu/d/9Z6mXc1) [Amazon Link](https://amzn.eu/d/9Z6mXc1)
### Pin-Konfiguration ### Pin-Konfiguration

View File

@ -6,14 +6,12 @@ FilaMan is a filament management system for 3D printing. It uses ESP32 hardware
Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface. Users can manage filament spools, monitor the status of the Automatic Material System (AMS) and make settings via a web interface.
The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format. The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management as well as the [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG format.
![Scale](./img/scale_trans.png) ![Scale](./img/scale_trans.png)
More Images can be found in the [img Folder](/img/) More Images can be found in the [img Folder](/img/)
or my website: [FilaMan Website](https://www.filaman.app) or my website:[FilaMan Website](https://www.filaman.app)
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU) german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki) ### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
@ -23,7 +21,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman). - **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman).
- **WiFi Connectivity:** WiFiManager for easy network configuration. - **WiFi Connectivity:** WiFiManager for easy network configuration.
- **MQTT Integration:** Connects to Bambu Lab printer for AMS control. - **MQTT Integration:** Connects to Bambu Lab printer for AMS control.
- **NFC-Tag NTAG213 NTAG215:** Use NTAG213, better NTAG215 because of enaught space on the Tag - **NFC-Tag NTAG215:** Use NTAG215 because of enaught space on the Tag
### Web Interface Features ### Web Interface Features
- **Real-time Updates:** WebSocket connection for live data updates. - **Real-time Updates:** WebSocket connection for live data updates.
@ -40,7 +38,6 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
- Filter and select filaments. - Filter and select filaments.
- Update spool weights automatically. - Update spool weights automatically.
- Track NFC tag assignments. - Track NFC tag assignments.
- Supports Spoolman Octoprint Plugin
### If you want to support my work, i would be happy to get a coffe ### If you want to support my work, i would be happy to get a coffe
<a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a> <a href="https://www.buymeacoffee.com/manuelw" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 108px !important;" ></a>
@ -67,7 +64,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
[Amazon Link](https://amzn.eu/d/0AuBp2c) [Amazon Link](https://amzn.eu/d/0AuBp2c)
- **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations. - **PN532 NFC NXP RFID-Modul V3:** For NFC tag operations.
[Amazon Link](https://amzn.eu/d/jfIuQXb) [Amazon Link](https://amzn.eu/d/jfIuQXb)
- **NFC Tags NTAG213 NTAG215:** RFID Tag - **NFC Tags Ntag215:** RFID Tag
[Amazon Link](https://amzn.eu/d/9Z6mXc1) [Amazon Link](https://amzn.eu/d/9Z6mXc1)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -640,6 +640,8 @@ function writeNfcTag() {
// Erstelle das NFC-Datenpaket mit korrekten Datentypen // Erstelle das NFC-Datenpaket mit korrekten Datentypen
const nfcData = { const nfcData = {
version: "2.0",
protocol: "openspool",
color_hex: selectedSpool.filament.color_hex || "FFFFFF", color_hex: selectedSpool.filament.color_hex || "FFFFFF",
type: selectedSpool.filament.material, type: selectedSpool.filament.material,
min_temp: minTemp, min_temp: minTemp,

View File

@ -52,18 +52,11 @@
if (spoolmanUrl && spoolmanUrl.trim() !== "") { if (spoolmanUrl && spoolmanUrl.trim() !== "") {
document.getElementById('spoolmanUrl').value = spoolmanUrl; document.getElementById('spoolmanUrl').value = spoolmanUrl;
} }
// Initialize OctoPrint fields visibility
toggleOctoFields();
}; };
function checkSpoolmanInstance() { function checkSpoolmanInstance() {
const url = document.getElementById('spoolmanUrl').value; const url = document.getElementById('spoolmanUrl').value;
const spoolmanOctoEnabled = document.getElementById('spoolmanOctoEnabled').checked; fetch(`/api/checkSpoolman?url=${encodeURIComponent(url)}`)
const spoolmanOctoUrl = document.getElementById('spoolmanOctoUrl').value;
const spoolmanOctoToken = document.getElementById('spoolmanOctoToken').value;
fetch(`/api/checkSpoolman?url=${encodeURIComponent(url)}&octoEnabled=${spoolmanOctoEnabled}&octoUrl=${spoolmanOctoUrl}&octoToken=${spoolmanOctoToken}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.healthy) { if (data.healthy) {
@ -97,15 +90,6 @@
document.getElementById('bambuStatusMessage').innerText = 'Error while saving: ' + error.message; document.getElementById('bambuStatusMessage').innerText = 'Error while saving: ' + error.message;
}); });
} }
/**
* Controls visibility of OctoPrint configuration fields based on checkbox state
* Called on page load and when checkbox changes
*/
function toggleOctoFields() {
const octoEnabled = document.getElementById('spoolmanOctoEnabled').checked;
document.getElementById('octoFields').style.display = octoEnabled ? 'block' : 'none';
}
</script> </script>
<script> <script>
var spoolmanUrl = "{{spoolmanUrl}}"; var spoolmanUrl = "{{spoolmanUrl}}";
@ -118,17 +102,6 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5> <h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5>
<input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port"> <input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port">
<h5 class="card-title">If you want to enable sending Spool to Spoolman Octoprint Plugin:</h5>
<p>
<input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin
</p>
<div id="octoFields" style="display: none;">
<p>
<input type="text" id="spoolmanOctoUrl" placeholder="http://ip-or-url-of-your-octoprint-instanz:port" value="{{spoolmanOctoUrl}}">
<input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}">
</p>
</div>
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button> <button onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
<p id="statusMessage"></p> <p id="statusMessage"></p>
</div> </div>
@ -154,11 +127,11 @@
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p> <p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>
<div class="input-group" style="display: flex; margin-bottom: 0;"> <div class="input-group" style="display: flex; margin-bottom: 0;">
<label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label> <label for="autoSend" style="width: 250px; margin-right: 5px;">Auto Send to Bambu:</label>
<label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait for Spool in Sec:</label> <label for="autoSendTime" style="width: 250px; margin-right: 5px;">Wait time in Seconds:</label>
</div> </div>
<div class="input-group" style="display: flex;"> <div class="input-group" style="display: flex;">
<input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;"> <input type="checkbox" id="autoSend" {{autoSendToBambu}} style="width: 190px; margin-right: 10px;">
<input type="number" min="60" id="autoSendTime" placeholder="Time to wait" value="{{autoSendTime}}" style="width: 100px;"> <input type="text" id="autoSendTime" placeholder="Time to wait for new Spool" value="{{autoSendTime}}" style="width: 100px;">
</div> </div>
<button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button> <button style="margin: 0;" onclick="saveBambuCredentials()">Save Bambu Credentials</button>

View File

@ -86,7 +86,7 @@ function populateVendorDropdown(data, selectedSmId = null) {
}); });
// Nach der Schleife: Formatierung der Gesamtlänge // Nach der Schleife: Formatierung der Gesamtlänge
console.log("Total Length: ", totalLength); console.log("Total Lenght: ", totalLength);
const formattedLength = totalLength > 1000 const formattedLength = totalLength > 1000
? (totalLength / 1000).toFixed(2) + " km" ? (totalLength / 1000).toFixed(2) + " km"
: totalLength.toFixed(2) + " m"; : totalLength.toFixed(2) + " m";
@ -97,15 +97,13 @@ function populateVendorDropdown(data, selectedSmId = null) {
? (weightInKg / 1000).toFixed(2) + " t" ? (weightInKg / 1000).toFixed(2) + " t"
: weightInKg.toFixed(2) + " kg"; : weightInKg.toFixed(2) + " kg";
// Dropdown mit gefilterten Herstellern befüllen - alphabetisch sortiert // Dropdown mit gefilterten Herstellern befüllen
Object.entries(filteredVendors) Object.entries(filteredVendors).forEach(([id, name]) => {
.sort(([, nameA], [, nameB]) => nameA.localeCompare(nameB)) // Sort vendors alphabetically by name const option = document.createElement("option");
.forEach(([id, name]) => { option.value = id;
const option = document.createElement("option"); option.textContent = name;
option.value = id; vendorSelect.appendChild(option);
option.textContent = name; });
vendorSelect.appendChild(option);
});
document.getElementById("totalSpools").textContent = totalSpools; document.getElementById("totalSpools").textContent = totalSpools;
document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag; document.getElementById("spoolsWithoutTag").textContent = spoolsWithoutTag;

View File

@ -188,18 +188,14 @@ label {
font-weight: bold; font-weight: bold;
} }
input[type="text"], input[type="submit"], input[type="number"] { input[type="text"], input[type="submit"] {
padding: 10px; padding: 10px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px; border-radius: 5px;
font-size: 16px; font-size: 16px;
} }
input[type="number"] { input[type="text"]:focus {
width: 108px !important;
}
input[type="text"]:focus, input[type="number"]:focus {
border-color: #007bff; border-color: #007bff;
outline: none; outline: none;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@ -9,9 +9,7 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[common] [common]
version = "1.4.0" version = "1.3.99"
to_old_version = "1.4.0"
## ##
[env:esp32dev] [env:esp32dev]
platform = espressif32 platform = espressif32
@ -50,7 +48,6 @@ build_flags =
#-DNDEBUG #-DNDEBUG
-mtext-section-literals -mtext-section-literals
-DVERSION=\"${common.version}\" -DVERSION=\"${common.version}\"
-DTOOLDVERSION=\"${common.to_old_version}\"
-DASYNCWEBSERVER_REGEX -DASYNCWEBSERVER_REGEX
#-DCORE_DEBUG_LEVEL=3 #-DCORE_DEBUG_LEVEL=3
-DCONFIG_ARDUHAL_LOG_COLORS=1 -DCONFIG_ARDUHAL_LOG_COLORS=1

View File

@ -5,15 +5,11 @@
bool spoolman_connected = false; bool spoolman_connected = false;
String spoolmanUrl = ""; String spoolmanUrl = "";
bool octoEnabled = false;
String octoUrl = "";
String octoToken = "";
struct SendToApiParams { struct SendToApiParams {
String httpType; String httpType;
String spoolsUrl; String spoolsUrl;
String updatePayload; String updatePayload;
String octoToken;
}; };
JsonDocument fetchSingleSpoolInfo(int spoolId) { JsonDocument fetchSingleSpoolInfo(int spoolId) {
@ -91,16 +87,14 @@ void sendToApi(void *parameter) {
String httpType = params->httpType; String httpType = params->httpType;
String spoolsUrl = params->spoolsUrl; String spoolsUrl = params->spoolsUrl;
String updatePayload = params->updatePayload; String updatePayload = params->updatePayload;
String octoToken = params->octoToken;
HTTPClient http; HTTPClient http;
http.begin(spoolsUrl); http.begin(spoolsUrl);
http.addHeader("Content-Type", "application/json"); http.addHeader("Content-Type", "application/json");
if (octoEnabled && octoToken != "") http.addHeader("X-Api-Key", octoToken);
int httpCode = http.PUT(updatePayload); int httpCode = http.PUT(updatePayload);
if (httpType == "PATCH") httpCode = http.PATCH(updatePayload); if (httpType == "PATCH") httpCode = http.PATCH(updatePayload);
if (httpType == "POST") httpCode = http.POST(updatePayload);
if (httpCode == HTTP_CODE_OK) { if (httpCode == HTTP_CODE_OK) {
Serial.println("Spoolman erfolgreich aktualisiert"); Serial.println("Spoolman erfolgreich aktualisiert");
@ -204,43 +198,6 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
return 1; return 1;
} }
bool updateSpoolOcto(int spoolId) {
String spoolsUrl = octoUrl + "/plugin/Spoolman/selectSpool";
Serial.print("Update Spule in Octoprint mit URL: ");
Serial.println(spoolsUrl);
JsonDocument updateDoc;
updateDoc["spool_id"] = spoolId;
updateDoc["tool"] = "tool0";
String updatePayload;
serializeJson(updateDoc, updatePayload);
Serial.print("Update Payload: ");
Serial.println(updatePayload);
SendToApiParams* params = new SendToApiParams();
if (params == nullptr) {
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return false;
}
params->httpType = "POST";
params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload;
params->octoToken = octoToken;
// Erstelle die Task
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
4096, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
NULL // Task-Handle (nicht benötigt)
);
return true;
}
bool updateSpoolBambuData(String payload) { bool updateSpoolBambuData(String payload) {
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload); DeserializationError error = deserializeJson(doc, payload);
@ -478,24 +435,17 @@ bool checkSpoolmanInstance(const String& url) {
return false; return false;
} }
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk) { bool saveSpoolmanUrl(const String& url) {
if (!checkSpoolmanInstance(url)) return false; if (!checkSpoolmanInstance(url)) return false;
JsonDocument doc; JsonDocument doc;
doc["url"] = url; doc["url"] = url;
doc["octoEnabled"] = octoOn; Serial.print("Speichere URL in Datei: ");
doc["octoUrl"] = octoWh; Serial.println(url);
doc["octoToken"] = octoTk;
Serial.print("Speichere Spoolman Data in Datei: ");
Serial.println(doc.as<String>());
if (!saveJsonValue("/spoolman_url.json", doc)) { if (!saveJsonValue("/spoolman_url.json", doc)) {
Serial.println("Fehler beim Speichern der Spoolman-URL."); Serial.println("Fehler beim Speichern der Spoolman-URL.");
return false;
} }
spoolmanUrl = url; spoolmanUrl = url;
octoEnabled = octoOn;
octoUrl = octoWh;
octoToken = octoTk;
return true; return true;
} }
@ -503,13 +453,6 @@ bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const
String loadSpoolmanUrl() { String loadSpoolmanUrl() {
JsonDocument doc; JsonDocument doc;
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) { if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) {
octoEnabled = (doc["octoEnabled"].is<bool>()) ? doc["octoEnabled"].as<bool>() : false;
if (octoEnabled && doc["octoToken"].is<String>() && doc["octoUrl"].is<String>())
{
octoUrl = doc["octoUrl"].as<String>();
octoToken = doc["octoToken"].as<String>();
}
return doc["url"].as<String>(); return doc["url"].as<String>();
} }
Serial.println("Keine gültige Spoolman-URL gefunden."); Serial.println("Keine gültige Spoolman-URL gefunden.");

View File

@ -9,12 +9,9 @@
extern bool spoolman_connected; extern bool spoolman_connected;
extern String spoolmanUrl; extern String spoolmanUrl;
extern bool octoEnabled;
extern String octoUrl;
extern String octoToken;
bool checkSpoolmanInstance(const String& url); bool checkSpoolmanInstance(const String& url);
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk); bool saveSpoolmanUrl(const String& url);
String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL
bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder
JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite JsonDocument fetchSingleSpoolInfo(int spoolId); // API-Funktion für die Webseite
@ -22,6 +19,5 @@ bool updateSpoolTagId(String uidString, const char* payload); // Neue Funktion z
uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts uint8_t updateSpoolWeight(String spoolId, uint16_t weight); // Neue Funktion zum Aktualisieren des Gewichts
bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman bool initSpoolman(); // Neue Funktion zum Initialisieren von Spoolman
bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten bool updateSpoolBambuData(String payload); // Neue Funktion zum Aktualisieren der Bambu-Daten
bool updateSpoolOcto(int spoolId); // Neue Funktion zum Aktualisieren der Octo-Daten
#endif #endif

View File

@ -341,7 +341,7 @@ void updateAmsWsData(JsonDocument& doc, JsonArray& amsArray, int& ams_count, Jso
ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>(); ams_data[i].trays[j].tray_color = trayObj["tray_color"].as<String>();
ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>(); ams_data[i].trays[j].nozzle_temp_min = trayObj["nozzle_temp_min"].as<int>();
ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>(); ams_data[i].trays[j].nozzle_temp_max = trayObj["nozzle_temp_max"].as<int>();
if (trayObj["tray_type"].as<String>() == "") ams_data[i].trays[j].setting_id = ""; //ams_data[i].trays[j].setting_id = trayObj["setting_id"].as<String>();
ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>(); ams_data[i].trays[j].cali_idx = trayObj["cali_idx"].as<String>();
} }
} }
@ -425,8 +425,16 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
return; 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<String>() == "push_status" && doc["print"]["ams"]["tray_pre"].as<uint8_t>()
&& !doc["print"]["ams"]["ams"].as<JsonArray>())
{
autoSetSpool(autoSetToBambuSpoolId, doc["print"]["ams"]["tray_pre"].as<uint8_t>());
}
// Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren // Prüfen, ob "print->upgrade_state" und "print.ams.ams" existieren
if (doc["print"]["upgrade_state"].is<JsonObject>() || (doc["print"]["command"].is<String>() && doc["print"]["command"] == "push_status")) if (doc["print"]["upgrade_state"].is<JsonObject>())
{ {
// Prüfen ob AMS-Daten vorhanden sind // Prüfen ob AMS-Daten vorhanden sind
if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>()) if (!doc["print"]["ams"].is<JsonObject>() || !doc["print"]["ams"]["ams"].is<JsonArray>())
@ -435,7 +443,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
} }
JsonArray amsArray = doc["print"]["ams"]["ams"].as<JsonArray>(); JsonArray amsArray = doc["print"]["ams"]["ams"].as<JsonArray>();
// Prüfe ob sich die AMS-Daten geändert haben // Prüfe ob sich die AMS-Daten geändert haben
bool hasChanges = false; bool hasChanges = false;
@ -471,12 +479,6 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
(trayObj["setting_id"].as<String>() != "" && trayObj["setting_id"].as<String>() != ams_data[storedIndex].trays[j].setting_id) || (trayObj["setting_id"].as<String>() != "" && trayObj["setting_id"].as<String>() != ams_data[storedIndex].trays[j].setting_id) ||
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) { trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
{
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
}
break; break;
} }
} }
@ -495,11 +497,6 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
(vtTray["setting_id"].as<String>() != "" && vtTray["setting_id"].as<String>() != ams_data[i].trays[0].setting_id) || (vtTray["setting_id"].as<String>() != "" && vtTray["setting_id"].as<String>() != ams_data[i].trays[0].setting_id) ||
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) { (vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges)
{
autoSetSpool(autoSetToBambuSpoolId, 254);
}
} }
break; break;
} }
@ -603,6 +600,7 @@ void mqtt_loop(void * parameter) {
bool setupMqtt() { bool setupMqtt() {
// Wenn Bambu Daten vorhanden // Wenn Bambu Daten vorhanden
bool success = loadBambuCredentials(); bool success = loadBambuCredentials();
vTaskDelay(100 / portTICK_PERIOD_MS);
if (!success) { if (!success) {
Serial.println("Failed to load Bambu credentials"); Serial.println("Failed to load Bambu credentials");
@ -666,7 +664,6 @@ bool setupMqtt() {
void bambu_restart() { void bambu_restart() {
if (BambuMqttTask) { if (BambuMqttTask) {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
delay(10);
} }
setupMqtt(); setupMqtt();
} }

View File

@ -197,11 +197,6 @@ void loop() {
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
weightSend = 1; weightSend = 1;
autoSetToBambuSpoolId = spoolId.toInt(); autoSetToBambuSpoolId = spoolId.toInt();
if (octoEnabled)
{
updateSpoolOcto(autoSetToBambuSpoolId);
}
} }
else else
{ {

View File

@ -44,6 +44,8 @@ void payloadToJson(uint8_t *data) {
DeserializationError error = deserializeJson(doc, jsonString); DeserializationError error = deserializeJson(doc, jsonString);
if (!error) { if (!error) {
const char* version = doc["version"];
const char* protocol = doc["protocol"];
const char* color_hex = doc["color_hex"]; const char* color_hex = doc["color_hex"];
const char* type = doc["type"]; const char* type = doc["type"];
int min_temp = doc["min_temp"]; int min_temp = doc["min_temp"];
@ -53,6 +55,8 @@ void payloadToJson(uint8_t *data) {
Serial.println(); Serial.println();
Serial.println("-----------------"); Serial.println("-----------------");
Serial.println("JSON-Parsed Data:"); Serial.println("JSON-Parsed Data:");
Serial.println(version);
Serial.println(protocol);
Serial.println(color_hex); Serial.println(color_hex);
Serial.println(type); Serial.println(type);
Serial.println(min_temp); Serial.println(min_temp);
@ -89,16 +93,8 @@ bool formatNdefTag() {
return success; return success;
} }
uint16_t readTagSize()
{
uint8_t buffer[4];
memset(buffer, 0, 4);
nfc.ntag2xx_ReadPage(3, buffer);
return buffer[2]*8;
}
uint8_t ntag2xx_WriteNDEF(const char *payload) { uint8_t ntag2xx_WriteNDEF(const char *payload) {
uint16_t tagSize = readTagSize(); uint8_t tagSize = 240; // 144 bytes is maximum for NTAG213
Serial.print("Tag Size: ");Serial.println(tagSize); Serial.print("Tag Size: ");Serial.println(tagSize);
uint8_t pageBuffer[4] = {0, 0, 0, 0}; uint8_t pageBuffer[4] = {0, 0, 0, 0};
@ -140,8 +136,6 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
if (combinedData == NULL) if (combinedData == NULL)
{ {
Serial.println("Fehler: Nicht genug Speicher vorhanden."); Serial.println("Fehler: Nicht genug Speicher vorhanden.");
oledShowMessage("Tag too small");
vTaskDelay(2000 / portTICK_PERIOD_MS);
return 0; return 0;
} }
@ -375,51 +369,46 @@ void scanRfidTask(void * parameter) {
if (uidLength == 7) if (uidLength == 7)
{ {
uint16_t tagSize = readTagSize(); uint8_t data[256];
if(tagSize > 0)
{
// Create a buffer depending on the size of the tag
uint8_t* data = (uint8_t*)malloc(tagSize);
memset(data, 0, tagSize);
// We probably have an NTAG2xx card (though it could be Ultralight as well) // We probably have an NTAG2xx card (though it could be Ultralight as well)
Serial.println("Seems to be an NTAG2xx tag (7 byte UID)"); Serial.println("Seems to be an NTAG2xx tag (7 byte UID)");
uint8_t numPages = readTagSize()/4; for (uint8_t i = 0; i < 45; i++) {
for (uint8_t i = 4; i < 4+numPages; i++) { /*
if (!nfc.ntag2xx_ReadPage(i, data+(i-4) * 4)) if (i < uidLength) {
{ uidString += String(uid[i], HEX);
break; // Stop if reading fails if (i < uidLength - 1) {
uidString += ":"; // Optional: Trennzeichen hinzufügen
} }
// Check for NDEF message end
if (data[(i - 4) * 4] == 0xFE)
{
break; // End of NDEF message
}
yield();
esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1));
} }
*/
if (!decodeNdefAndReturnJson(data)) if (!nfc.mifareclassic_ReadDataBlock(i, data + (i - 4) * 4))
{ {
oledShowMessage("NFC-Tag unknown"); break; // Stop if reading fails
vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 2;
} }
else // Check for NDEF message end
if (data[(i - 4) * 4] == 0xFE)
{ {
hasReadRfidTag = 1; break; // End of NDEF message
} }
free(data); yield();
esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1));
} }
else
if (!decodeNdefAndReturnJson(data))
{ {
oledShowMessage("NFC-Tag read error"); oledShowMessage("NFC-Tag unknown");
vTaskDelay(2000 / portTICK_PERIOD_MS);
hasReadRfidTag = 2; hasReadRfidTag = 2;
} }
else
{
hasReadRfidTag = 1;
}
} }
else else
{ {

View File

@ -14,34 +14,6 @@ static size_t updateTotalSize = 0;
static size_t updateWritten = 0; static size_t updateWritten = 0;
static bool isSpiffsUpdate = false; static bool isSpiffsUpdate = false;
/**
* Compares two version strings and determines if version1 is less than version2
*
* @param version1 First version string (format: x.y.z)
* @param version2 Second version string (format: x.y.z)
* @return true if version1 is less than version2
*/
bool isVersionLessThan(const String& version1, const String& version2) {
int major1 = 0, minor1 = 0, patch1 = 0;
int major2 = 0, minor2 = 0, patch2 = 0;
// Parse version1
sscanf(version1.c_str(), "%d.%d.%d", &major1, &minor1, &patch1);
// Parse version2
sscanf(version2.c_str(), "%d.%d.%d", &major2, &minor2, &patch2);
// Compare major version
if (major1 < major2) return true;
if (major1 > major2) return false;
// Major versions equal, compare minor
if (minor1 < minor2) return true;
if (minor1 > minor2) return false;
// Minor versions equal, compare patch
return patch1 < patch2;
}
void backupJsonConfigs() { void backupJsonConfigs() {
// Bambu Credentials backup // Bambu Credentials backup
@ -139,16 +111,6 @@ void handleUpdate(AsyncWebServer &server) {
updateHandler->setUri("/update"); updateHandler->setUri("/update");
updateHandler->setMethod(HTTP_POST); updateHandler->setMethod(HTTP_POST);
// Check if current version is less than defined TOOLVERSION before proceeding with update
if (isVersionLessThan(VERSION, TOOLDVERSION)) {
updateHandler->onRequest([](AsyncWebServerRequest *request) {
request->send(400, "application/json",
"{\"success\":false,\"message\":\"Your current version is too old. Please perform a full upgrade.\"}");
});
server.addHandler(updateHandler);
return;
}
updateHandler->onUpload([](AsyncWebServerRequest *request, String filename, updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
size_t index, uint8_t *data, size_t len, bool final) { size_t index, uint8_t *data, size_t len, bool final) {
if (!index) { if (!index) {

View File

@ -47,7 +47,7 @@ void scale_loop(void * parameter) {
weight = round(scale.get_units()); weight = round(scale.get_units());
} }
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100)); // Verzögerung, um die CPU nicht zu überlasten
} }
} }
@ -90,7 +90,7 @@ uint8_t start_scale() {
BaseType_t result = xTaskCreatePinnedToCore( BaseType_t result = xTaskCreatePinnedToCore(
scale_loop, /* Function to implement the task */ scale_loop, /* Function to implement the task */
"ScaleLoop", /* Name of the task */ "ScaleLoop", /* Name of the task */
2048, /* Stack size in words */ 10000, /* Stack size in words */
NULL, /* Task input parameter */ NULL, /* Task input parameter */
scaleTaskPrio, /* Priority of the task */ scaleTaskPrio, /* Priority of the task */
&ScaleTask, /* Task handle. */ &ScaleTask, /* Task handle. */
@ -110,7 +110,6 @@ uint8_t calibrate_scale() {
//vTaskSuspend(RfidReaderTask); //vTaskSuspend(RfidReaderTask);
vTaskDelete(RfidReaderTask); vTaskDelete(RfidReaderTask);
vTaskDelete(ScaleTask);
pauseBambuMqttTask = true; pauseBambuMqttTask = true;
pauseMainTask = 1; pauseMainTask = 1;
@ -178,6 +177,8 @@ uint8_t calibrate_scale() {
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
//ESP.restart();
} }
else else
{ {
@ -210,8 +211,9 @@ uint8_t calibrate_scale() {
} }
oledShowMessage("Scale Ready"); oledShowMessage("Scale Ready");
Serial.println("restart Scale Task"); Serial.println("starte Scale Task");
start_scale(); start_scale();
pauseBambuMqttTask = false; pauseBambuMqttTask = false;

View File

@ -239,10 +239,7 @@ void setupWebserver(AsyncWebServer &server) {
server.on("/spoolman", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/spoolman", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /spoolman erhalten"); Serial.println("Anfrage für /spoolman erhalten");
String html = loadHtmlWithHeader("/spoolman.html"); String html = loadHtmlWithHeader("/spoolman.html");
html.replace("{{spoolmanUrl}}", (spoolmanUrl != "") ? spoolmanUrl : ""); html.replace("{{spoolmanUrl}}", spoolmanUrl);
html.replace("{{spoolmanOctoEnabled}}", octoEnabled ? "checked" : "");
html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : "");
html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : "");
JsonDocument doc; JsonDocument doc;
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>())
@ -280,21 +277,10 @@ void setupWebserver(AsyncWebServer &server) {
return; return;
} }
if (request->getParam("octoEnabled")->value() == "true" && (!request->hasParam("octoUrl") || !request->hasParam("octoToken"))) {
request->send(400, "application/json", "{\"success\": false, \"error\": \"Missing OctoPrint URL or Token parameter\"}");
return;
}
String url = request->getParam("url")->value(); String url = request->getParam("url")->value();
bool octoEnabled = (request->getParam("octoEnabled")->value() == "true") ? true : false;
String octoUrl = request->getParam("octoUrl")->value();
String octoToken = (request->getParam("octoToken")->value() != "") ? request->getParam("octoToken")->value() : "";
url.trim(); url.trim();
octoUrl.trim();
octoToken.trim();
bool healthy = saveSpoolmanUrl(url, octoEnabled, octoUrl, octoToken); bool healthy = saveSpoolmanUrl(url);
String jsonResponse = "{\"healthy\": " + String(healthy ? "true" : "false") + "}"; String jsonResponse = "{\"healthy\": " + String(healthy ? "true" : "false") + "}";
request->send(200, "application/json", jsonResponse); request->send(200, "application/json", jsonResponse);