mirror of
https://github.com/tugsi/Filaman.git
synced 2026-07-09 18:02:48 +02:00
Compare commits
16 Commits
v1.4.1
...
f1a9c3c5d4
| Author | SHA1 | Date | |
|---|---|---|---|
| f1a9c3c5d4 | |||
| ccca159a10 | |||
| 70c8b7bdc0 | |||
| c574cc964e | |||
| 9262865b56 | |||
| 3eac0e5ac4 | |||
| 24d91693d9 | |||
| f7c01eb5f0 | |||
| a41ef9e75a | |||
| 659dfb8627 | |||
| b4dd0d357a | |||
| 8e38a68191 | |||
| c3ec7edf6a | |||
| a35e13e015 | |||
| e0df5d33f2 | |||
| 94c26590c8 |
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
+10
-11
@@ -1,33 +1,32 @@
|
||||
# FilaMan - Filament Management System
|
||||
|
||||
FilaMan ist ein Filament-Managementsystem für den 3D-Druck. Es verwendet ESP32-Hardware für Gewichtsmessungen und NFC-Tag-Management.
|
||||
Benutzer können Filamentspulen verwalten, den Status des Automatic Material System (AMS) von Bablulab Druckern überwachen und Einstellungen über eine Weboberfläche vornehmen.
|
||||
Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/Spoolman) Filamentverwaltung, zusätzlich mit [Bambulab](https://bambulab.com/en-us) 3D-Druckern und sowie dem [Openspool](https://github.com/spuder/OpenSpool) NFC-TAG Format.
|
||||
Benutzer können Filamentspulen verwalten, den Status des Automatic Material System (AMS) von Bambu Lab Druckern überwachen und Einstellungen über eine Weboberfläche vornehmen.
|
||||
Das System integriert sich nahtlos mit der [Spoolman](https://github.com/Donkie/Spoolman) Filamentverwaltung, zusätzlich mit [Bambu Lab](https://bambulab.com/en-us) 3D-Druckern.
|
||||
|
||||

|
||||
|
||||
Weitere Bilder finden Sie im [img Ordner](/img/)
|
||||
oder auf meiner Website: [FilaMan Website](https://www.filaman.app)
|
||||
Deutsches Erklärvideo: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
|
||||
Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
|
||||
### Es gibt jetzt auch ein Wiki, dort sind nochmal alle Funktionen beschrieben: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
|
||||
|
||||
### ESP32 Hardware-Funktionen
|
||||
- **Gewichtsmessung:** Verwendung einer Wägezelle mit HX711-Verstärker für präzise Gewichtsverfolgung.
|
||||
- **NFC-Tag Lesen/Schreiben:** PN532-Modul zum Lesen und Schreiben von Filamentdaten auf NFC-Tags.
|
||||
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, Bambu Lab, Spoolman).
|
||||
- **OLED-Display:** Zeigt aktuelles Gewicht, Verbindungsstatus (WiFi, BambuLab, Spoolman).
|
||||
- **WLAN-Konnektivität:** WiFiManager für einfache Netzwerkkonfiguration.
|
||||
- **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 NTAG213 NTAG215:** Verwendung von NTAG213 (144 Bytes - Kapazität), besser NTAG215 (540 Bytes - Kapazität) wegen ausreichendem Speicherplatz auf dem Tag
|
||||
|
||||
### Weboberflächen-Funktionen
|
||||
- **Echtzeit-Updates:** WebSocket-Verbindung für Live-Daten-Updates.
|
||||
- **NFC-Tag-Verwaltung:**
|
||||
- Filamentdaten auf NFC-Tags schreiben.
|
||||
- Verwendet das NFC-Tag-Format von [Openspool](https://github.com/spuder/OpenSpool)
|
||||
- Ermöglicht automatische Spulenerkennung im AMS
|
||||
- **Bambulab AMS-Integration:**
|
||||
- Ermöglicht automatische Spulenerkennung im AMS.
|
||||
- **Bambu Lab AMS-Integration:**
|
||||
- Anzeige der aktuellen AMS-Fachbelegung.
|
||||
- Zuordnung von Filamenten zu AMS-Slots.
|
||||
- Unterstützung für externe Spulenhalter.
|
||||
@@ -36,7 +35,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
|
||||
- Filtern und Auswählen von Filamenten.
|
||||
- Automatische Aktualisierung der Spulengewichte.
|
||||
- Verfolgung von NFC-Tag-Zuweisungen.
|
||||
- Unterstützt das Spoolman Octoprint Plugin
|
||||
- Unterstützt das Spoolman Octoprint Plugin.
|
||||
|
||||
### 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>
|
||||
@@ -122,7 +121,7 @@ Du musst Spoolman auf DEBUG Modus setzten, da man bisher in Spoolman keine CORS
|
||||
```
|
||||
|
||||
## Schritt-für-Schritt Installation
|
||||
### Einfache Installation
|
||||
### Einfache Installation (Google Chrome oder Microsoft Edge)
|
||||
1. **Gehe auf [FilaMan Installer](https://www.filaman.app/installer.html)**
|
||||
|
||||
2. **Stecke dein ESP an den Rechner und klicke Connect**
|
||||
@@ -185,4 +184,4 @@ Dieses Projekt ist unter der MIT-Lizenz lizenziert. Siehe [LICENSE](LICENSE) Dat
|
||||
Der Code kann getestet und die Anwendung kann vom [GitHub Repository](https://github.com/ManuelW77/Filaman) heruntergeladen werden.
|
||||
|
||||
### 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>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
FilaMan is a filament management system for 3D printing. It uses ESP32 hardware for weight measurement and NFC tag management.
|
||||
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 [Bambu Lab](https://bambulab.com/en-us) 3D printers and [Spoolman](https://github.com/Donkie/Spoolman) filament management.
|
||||
|
||||
|
||||

|
||||
@@ -13,7 +13,7 @@ The system integrates seamlessly with [Bambulab](https://bambulab.com/en-us) 3D
|
||||
More Images can be found in the [img Folder](/img/)
|
||||
or my website: [FilaMan Website](https://www.filaman.app)
|
||||
german explanatory video: [Youtube](https://youtu.be/uNDe2wh9SS8?si=b-jYx4I1w62zaOHU)
|
||||
Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
|
||||
Discord Server: [https://discord.gg/my7Gvaxj2v](https://discord.gg/my7Gvaxj2v)
|
||||
|
||||
### Now more detailed informations about the usage: [Wiki](https://github.com/ManuelW77/Filaman/wiki)
|
||||
|
||||
@@ -23,13 +23,12 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
|
||||
- **OLED Display:** Shows current weight, connection status (WiFi, Bambu Lab, Spoolman).
|
||||
- **WiFi Connectivity:** WiFiManager for easy network configuration.
|
||||
- **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 NTAG213 NTAG215:** Use NTAG213 (Capacity of 144 bytes), better NTAG215 (Capacity of 540 bytes) because of enaught space on the Tag.
|
||||
|
||||
### Web Interface Features
|
||||
- **Real-time Updates:** WebSocket connection for live data updates.
|
||||
- **NFC Tag Management:**
|
||||
- Write filament data to NFC tags.
|
||||
- uses NFC-Tag Format of [Openspool](https://github.com/spuder/OpenSpool)
|
||||
- so you can use it with automatic Spool detection in AMS
|
||||
- **Bambulab AMS Integration:**
|
||||
- Display current AMS tray contents.
|
||||
@@ -40,7 +39,7 @@ Discord Server: [https://discord.gg/vMAx2gf5](https://discord.gg/vMAx2gf5)
|
||||
- Filter and select filaments.
|
||||
- Update spool weights automatically.
|
||||
- Track NFC tag assignments.
|
||||
- Supports Spoolman Octoprint Plugin
|
||||
- Supports Spoolman Octoprint Plugin.
|
||||
|
||||
### 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>
|
||||
@@ -128,7 +127,7 @@ You have to activate Spoolman in debug mode, because you are not able to set COR
|
||||
|
||||
|
||||
## Step-by-Step Installation
|
||||
### Easy Installation
|
||||
### Easy Installation (Google Chrome or Microsoft Edge)
|
||||
1. **Go to [FilaMan Installer](https://www.filaman.app/installer.html)**
|
||||
|
||||
2. **Plug you device in and push Connect button**
|
||||
@@ -191,4 +190,4 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
|
||||
The code can be tested and the application can be downloaded from the [GitHub repository](https://github.com/ManuelW77/Filaman).
|
||||
|
||||
### 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>
|
||||
|
||||
+55
-55
@@ -11,7 +11,7 @@ let reconnectTimer = null;
|
||||
// WebSocket Funktionen
|
||||
function startHeartbeat() {
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
|
||||
|
||||
heartbeatTimer = setInterval(() => {
|
||||
// Prüfe ob zu lange keine Antwort kam
|
||||
if (Date.now() - lastHeartbeatResponse > HEARTBEAT_TIMEOUT) {
|
||||
@@ -29,7 +29,7 @@ function startHeartbeat() {
|
||||
updateConnectionStatus();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Sende Heartbeat
|
||||
socket.send(JSON.stringify({ type: 'heartbeat' }));
|
||||
@@ -59,18 +59,18 @@ function initWebSocket() {
|
||||
|
||||
try {
|
||||
socket = new WebSocket('ws://' + window.location.host + '/ws');
|
||||
|
||||
socket.onopen = function() {
|
||||
|
||||
socket.onopen = function () {
|
||||
isConnected = true;
|
||||
updateConnectionStatus();
|
||||
startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung
|
||||
};
|
||||
|
||||
socket.onclose = function() {
|
||||
|
||||
socket.onclose = function () {
|
||||
isConnected = false;
|
||||
updateConnectionStatus();
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
|
||||
|
||||
// Nur neue Verbindung versuchen, wenn kein Timer läuft
|
||||
if (!reconnectTimer) {
|
||||
reconnectTimer = setTimeout(() => {
|
||||
@@ -78,22 +78,22 @@ function initWebSocket() {
|
||||
}, RECONNECT_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
socket.onerror = function(error) {
|
||||
|
||||
socket.onerror = function (error) {
|
||||
isConnected = false;
|
||||
updateConnectionStatus();
|
||||
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
||||
|
||||
|
||||
// Bei Fehler Verbindung schließen und neu aufbauen
|
||||
if (socket) {
|
||||
socket.close();
|
||||
socket = null;
|
||||
}
|
||||
};
|
||||
|
||||
socket.onmessage = function(event) {
|
||||
|
||||
socket.onmessage = function (event) {
|
||||
lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort
|
||||
|
||||
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'amsData') {
|
||||
displayAmsData(data.payload);
|
||||
@@ -109,13 +109,13 @@ function initWebSocket() {
|
||||
const bambuDot = document.getElementById('bambuDot');
|
||||
const spoolmanDot = document.getElementById('spoolmanDot');
|
||||
const ramStatus = document.getElementById('ramStatus');
|
||||
|
||||
|
||||
if (bambuDot) {
|
||||
bambuDot.className = 'status-dot ' + (data.bambu_connected ? 'online' : 'offline');
|
||||
// Add click handler only when offline
|
||||
if (!data.bambu_connected) {
|
||||
bambuDot.style.cursor = 'pointer';
|
||||
bambuDot.onclick = function() {
|
||||
bambuDot.onclick = function () {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({
|
||||
type: 'reconnect',
|
||||
@@ -133,7 +133,7 @@ function initWebSocket() {
|
||||
// Add click handler only when offline
|
||||
if (!data.spoolman_connected) {
|
||||
spoolmanDot.style.cursor = 'pointer';
|
||||
spoolmanDot.onclick = function() {
|
||||
spoolmanDot.onclick = function () {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({
|
||||
type: 'reconnect',
|
||||
@@ -161,7 +161,7 @@ function initWebSocket() {
|
||||
} catch (error) {
|
||||
isConnected = false;
|
||||
updateConnectionStatus();
|
||||
|
||||
|
||||
// Nur neue Verbindung versuchen, wenn kein Timer läuft
|
||||
if (!reconnectTimer) {
|
||||
reconnectTimer = setTimeout(() => {
|
||||
@@ -189,26 +189,26 @@ function updateConnectionStatus() {
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initWebSocket();
|
||||
|
||||
|
||||
// Event Listener für Checkbox
|
||||
document.getElementById("onlyWithoutSmId").addEventListener("change", function() {
|
||||
document.getElementById("onlyWithoutSmId").addEventListener("change", function () {
|
||||
const spoolsData = window.getSpoolData();
|
||||
window.populateVendorDropdown(spoolsData);
|
||||
});
|
||||
});
|
||||
|
||||
// Event Listener für Spoolman Events
|
||||
document.addEventListener('spoolDataLoaded', function(event) {
|
||||
document.addEventListener('spoolDataLoaded', function (event) {
|
||||
window.populateVendorDropdown(event.detail);
|
||||
});
|
||||
|
||||
document.addEventListener('spoolmanError', function(event) {
|
||||
document.addEventListener('spoolmanError', function (event) {
|
||||
showNotification(`Spoolman Error: ${event.detail.message}`, false);
|
||||
});
|
||||
|
||||
document.addEventListener('filamentSelected', function(event) {
|
||||
document.addEventListener('filamentSelected', function (event) {
|
||||
updateNfcInfo();
|
||||
// Zeige Spool-Buttons wenn ein Filament ausgewählt wurde
|
||||
const selectedText = document.getElementById("selected-filament").textContent;
|
||||
@@ -218,13 +218,13 @@ document.addEventListener('filamentSelected', function(event) {
|
||||
// Hilfsfunktion für kontrastreiche Textfarbe
|
||||
function getContrastColor(hexcolor) {
|
||||
// Konvertiere Hex zu RGB
|
||||
const r = parseInt(hexcolor.substr(0,2),16);
|
||||
const g = parseInt(hexcolor.substr(2,2),16);
|
||||
const b = parseInt(hexcolor.substr(4,2),16);
|
||||
|
||||
const r = parseInt(hexcolor.substr(0, 2), 16);
|
||||
const g = parseInt(hexcolor.substr(2, 2), 16);
|
||||
const b = parseInt(hexcolor.substr(4, 2), 16);
|
||||
|
||||
// Berechne Helligkeit (YIQ Formel)
|
||||
const yiq = ((r*299)+(g*587)+(b*114))/1000;
|
||||
|
||||
const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
|
||||
|
||||
// Return schwarz oder weiß basierend auf Helligkeit
|
||||
return (yiq >= 128) ? '#000000' : '#FFFFFF';
|
||||
}
|
||||
@@ -242,7 +242,7 @@ function updateNfcInfo() {
|
||||
}
|
||||
|
||||
// Finde die ausgewählte Spule in den Daten
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
|
||||
);
|
||||
|
||||
@@ -255,18 +255,18 @@ function updateNfcInfo() {
|
||||
|
||||
function displayAmsData(amsData) {
|
||||
const amsDataContainer = document.getElementById('amsData');
|
||||
amsDataContainer.innerHTML = '';
|
||||
amsDataContainer.innerHTML = '';
|
||||
|
||||
amsData.forEach((ams) => {
|
||||
// Bestimme den Anzeigenamen für das AMS
|
||||
const amsDisplayName = ams.ams_id === 255 ? 'External Spool' : `AMS ${ams.ams_id}`;
|
||||
|
||||
|
||||
const trayHTML = ams.tray.map(tray => {
|
||||
// Prüfe ob überhaupt Daten vorhanden sind
|
||||
const relevantFields = ['tray_type', 'tray_sub_brands', 'tray_info_idx', 'setting_id', 'cali_idx'];
|
||||
const hasAnyContent = relevantFields.some(field =>
|
||||
tray[field] !== null &&
|
||||
tray[field] !== undefined &&
|
||||
const hasAnyContent = relevantFields.some(field =>
|
||||
tray[field] !== null &&
|
||||
tray[field] !== undefined &&
|
||||
tray[field] !== '' &&
|
||||
tray[field] !== 'null'
|
||||
);
|
||||
@@ -282,8 +282,8 @@ function displayAmsData(amsData) {
|
||||
cursor: pointer; display: none;">
|
||||
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;">
|
||||
</button>`;
|
||||
|
||||
// Nur für nicht-leere Trays den Button-HTML erstellen
|
||||
|
||||
// Nur für nicht-leere Trays den Button-HTML erstellen
|
||||
const outButtonHtml = `
|
||||
<button class="spool-button" onclick="handleSpoolOut()"
|
||||
style="position: absolute; top: -35px; right: -15px;
|
||||
@@ -313,7 +313,7 @@ function displayAmsData(amsData) {
|
||||
}
|
||||
|
||||
// Generiere den Type mit Color-Box zusammen
|
||||
const typeWithColor = tray.tray_type ?
|
||||
const typeWithColor = tray.tray_type ?
|
||||
`<p>Typ: ${tray.tray_type} ${tray.tray_color ? `<span style="
|
||||
background-color: #${tray.tray_color};
|
||||
width: 20px;
|
||||
@@ -334,9 +334,9 @@ function displayAmsData(amsData) {
|
||||
|
||||
// Nur gültige Felder anzeigen
|
||||
const trayDetails = trayProperties
|
||||
.filter(prop =>
|
||||
tray[prop.key] !== null &&
|
||||
tray[prop.key] !== undefined &&
|
||||
.filter(prop =>
|
||||
tray[prop.key] !== null &&
|
||||
tray[prop.key] !== undefined &&
|
||||
tray[prop.key] !== '' &&
|
||||
tray[prop.key] !== 'null'
|
||||
)
|
||||
@@ -350,7 +350,7 @@ function displayAmsData(amsData) {
|
||||
.join('');
|
||||
|
||||
// Temperaturen nur anzeigen, wenn beide nicht 0 sind
|
||||
const tempHTML = (tray.nozzle_temp_min > 0 && tray.nozzle_temp_max > 0)
|
||||
const tempHTML = (tray.nozzle_temp_min > 0 && tray.nozzle_temp_max > 0)
|
||||
? `<p>Nozzle Temp: ${tray.nozzle_temp_min}°C - ${tray.nozzle_temp_max}°C</p>`
|
||||
: '';
|
||||
|
||||
@@ -376,7 +376,7 @@ function displayAmsData(amsData) {
|
||||
${trayHTML}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
amsDataContainer.innerHTML += amsInfo;
|
||||
});
|
||||
}
|
||||
@@ -394,7 +394,7 @@ function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp
|
||||
const selectedText = document.getElementById("selected-filament").textContent;
|
||||
|
||||
// Finde die ausgewählte Spule in den Daten
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
|
||||
);
|
||||
|
||||
@@ -460,7 +460,7 @@ function handleSpoolIn(amsId, trayId) {
|
||||
}
|
||||
|
||||
// Finde die ausgewählte Spule in den Daten
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
|
||||
);
|
||||
|
||||
@@ -473,7 +473,7 @@ function handleSpoolIn(amsId, trayId) {
|
||||
let minTemp = "175";
|
||||
let maxTemp = "275";
|
||||
|
||||
if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
|
||||
if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
|
||||
selectedSpool.filament.nozzle_temperature.length >= 2) {
|
||||
minTemp = selectedSpool.filament.nozzle_temperature[0];
|
||||
maxTemp = selectedSpool.filament.nozzle_temperature[1];
|
||||
@@ -490,7 +490,7 @@ function handleSpoolIn(amsId, trayId) {
|
||||
nozzle_temp_max: parseInt(maxTemp),
|
||||
type: selectedSpool.filament.material,
|
||||
brand: selectedSpool.filament.vendor.name,
|
||||
tray_info_idx: selectedSpool.filament.extra.bambu_idx.replace(/['"]+/g, '').trim(),
|
||||
tray_info_idx: selectedSpool.filament.extra.bambu_idx?.replace(/['"]+/g, '').trim() || '',
|
||||
cali_idx: "-1" // Default-Wert setzen
|
||||
}
|
||||
};
|
||||
@@ -518,7 +518,7 @@ function handleSpoolIn(amsId, trayId) {
|
||||
|
||||
function updateNfcStatusIndicator(data) {
|
||||
const indicator = document.getElementById('nfcStatusIndicator');
|
||||
|
||||
|
||||
if (data.found === 0) {
|
||||
// Kein NFC Tag gefunden
|
||||
indicator.className = 'status-circle';
|
||||
@@ -534,7 +534,7 @@ function updateNfcStatusIndicator(data) {
|
||||
function updateNfcData(data) {
|
||||
// Den Container für den NFC Status finden
|
||||
const nfcStatusContainer = document.querySelector('.nfc-status-display');
|
||||
|
||||
|
||||
// Bestehende Daten-Anzeige entfernen falls vorhanden
|
||||
const existingData = nfcStatusContainer.querySelector('.nfc-data');
|
||||
if (existingData) {
|
||||
@@ -593,7 +593,7 @@ function updateNfcData(data) {
|
||||
if (matchingSpool) {
|
||||
// Zuerst Hersteller-Dropdown aktualisieren
|
||||
document.getElementById("vendorSelect").value = matchingSpool.filament.vendor.id;
|
||||
|
||||
|
||||
// Dann Filament-Dropdown aktualisieren und Spule auswählen
|
||||
updateFilamentDropdown();
|
||||
setTimeout(() => {
|
||||
@@ -606,7 +606,7 @@ function updateNfcData(data) {
|
||||
html += '</div>';
|
||||
nfcDataDiv.innerHTML = html;
|
||||
|
||||
|
||||
|
||||
// Neues div zum Container hinzufügen
|
||||
nfcStatusContainer.appendChild(nfcDataDiv);
|
||||
}
|
||||
@@ -619,7 +619,7 @@ function writeNfcTag() {
|
||||
}
|
||||
|
||||
const spoolsData = window.getSpoolData();
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
const selectedSpool = spoolsData.find(spool =>
|
||||
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
|
||||
);
|
||||
|
||||
@@ -631,8 +631,8 @@ function writeNfcTag() {
|
||||
// Temperaturwerte korrekt extrahieren
|
||||
let minTemp = "175";
|
||||
let maxTemp = "275";
|
||||
|
||||
if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
|
||||
|
||||
if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
|
||||
selectedSpool.filament.nozzle_temperature.length >= 2) {
|
||||
minTemp = String(selectedSpool.filament.nozzle_temperature[0]);
|
||||
maxTemp = String(selectedSpool.filament.nozzle_temperature[1]);
|
||||
@@ -686,4 +686,4 @@ function showNotification(message, isSuccess) {
|
||||
notification.remove();
|
||||
}, 300);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
+155
-45
@@ -18,7 +18,7 @@ PubSubClient client(sslClient);
|
||||
TaskHandle_t BambuMqttTask;
|
||||
|
||||
String report_topic = "";
|
||||
//String request_topic = "";
|
||||
String request_topic = "";
|
||||
const char* bambu_username = "bblp";
|
||||
const char* bambu_ip = nullptr;
|
||||
const char* bambu_accesscode = nullptr;
|
||||
@@ -92,7 +92,7 @@ bool loadBambuCredentials() {
|
||||
bambu_serialnr = g_bambu_serialnr.c_str();
|
||||
|
||||
report_topic = "device/" + String(bambu_serialnr) + "/report";
|
||||
//request_topic = "device/" + String(bambu_serialnr) + "/request";
|
||||
request_topic = "device/" + String(bambu_serialnr) + "/request";
|
||||
return true;
|
||||
}
|
||||
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
|
||||
@@ -197,18 +197,69 @@ FilamentResult findFilamentIdx(String brand, String type) {
|
||||
}
|
||||
|
||||
bool sendMqttMessage(const String& payload) {
|
||||
Serial.println("Sending MQTT message");
|
||||
Serial.println(payload);
|
||||
if (client.publish(report_topic.c_str(), payload.c_str()))
|
||||
{
|
||||
return true;
|
||||
// Check MQTT client state first
|
||||
if (!client.connected()) {
|
||||
Serial.println("Error: MQTT client not connected when trying to send message");
|
||||
Serial.print("MQTT client state: ");
|
||||
Serial.println(client.state());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if request_topic is empty
|
||||
if (request_topic.isEmpty()) {
|
||||
Serial.println("Error: request_topic is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check payload
|
||||
if (payload.isEmpty()) {
|
||||
Serial.println("Error: Empty payload!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.println("Attempting to send MQTT message:");
|
||||
Serial.println("Topic: " + request_topic);
|
||||
Serial.println("Payload: " + payload);
|
||||
Serial.println("Payload length: " + String(payload.length()));
|
||||
|
||||
return false;
|
||||
// Try to publish with QoS 1 and retain flag false
|
||||
bool published = client.publish(request_topic.c_str(), payload.c_str(), false);
|
||||
|
||||
if (published) {
|
||||
Serial.println("MQTT message published successfully");
|
||||
return true;
|
||||
} else {
|
||||
Serial.println("Failed to publish MQTT message");
|
||||
Serial.print("MQTT client state after publish attempt: ");
|
||||
Serial.println(client.state());
|
||||
|
||||
// Additional error information
|
||||
switch(client.state()) {
|
||||
case -4:
|
||||
Serial.println("Reason: Connection timeout");
|
||||
break;
|
||||
case -3:
|
||||
Serial.println("Reason: Connection lost");
|
||||
break;
|
||||
case -2:
|
||||
Serial.println("Reason: Connect failed");
|
||||
break;
|
||||
case -1:
|
||||
Serial.println("Reason: Disconnected");
|
||||
break;
|
||||
case 0:
|
||||
Serial.println("Reason: Connected but publish failed");
|
||||
break;
|
||||
default:
|
||||
Serial.println("Reason: Unknown error");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool setBambuSpool(String payload) {
|
||||
Serial.println("Spool settings in");
|
||||
Serial.println("Spool settings received from WebSocket");
|
||||
Serial.println(payload);
|
||||
|
||||
// Parse the JSON
|
||||
@@ -220,21 +271,28 @@ bool setBambuSpool(String payload) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int amsId = doc["amsId"];
|
||||
int trayId = doc["trayId"];
|
||||
// Check if we have all required fields
|
||||
if (!doc["amsId"].is<int>() || !doc["trayId"].is<int>()) {
|
||||
Serial.println("Error: Missing or invalid required fields in payload");
|
||||
return false;
|
||||
}
|
||||
|
||||
int amsId = doc["amsId"].as<int>();
|
||||
int trayId = doc["trayId"].as<int>();
|
||||
String color = doc["color"].as<String>();
|
||||
color.toUpperCase();
|
||||
int minTemp = doc["nozzle_temp_min"];
|
||||
int maxTemp = doc["nozzle_temp_max"];
|
||||
String type = doc["type"].as<String>();
|
||||
int minTemp = doc["nozzle_temp_min"] | 0; // Default to 0 if not present
|
||||
int maxTemp = doc["nozzle_temp_max"] | 0; // Default to 0 if not present
|
||||
String type = doc["type"] | ""; // Default to empty string if not present
|
||||
(type == "PLA+") ? type = "PLA" : type;
|
||||
String brand = doc["brand"].as<String>();
|
||||
String brand = doc["brand"] | ""; // Default to empty string if not present
|
||||
String tray_info_idx = (doc["tray_info_idx"].as<String>() != "-1") ? doc["tray_info_idx"].as<String>() : "";
|
||||
if (tray_info_idx == "") {
|
||||
if (brand != "" && type != "") {
|
||||
FilamentResult result = findFilamentIdx(brand, type);
|
||||
tray_info_idx = result.key;
|
||||
type = result.type; // Aktualisiere den type mit dem gefundenen Basistyp
|
||||
Serial.println("Found filament idx: " + tray_info_idx + " for type: " + type);
|
||||
}
|
||||
}
|
||||
String setting_id = doc["bambu_setting_id"].as<String>();
|
||||
@@ -242,6 +300,7 @@ bool setBambuSpool(String payload) {
|
||||
|
||||
doc.clear();
|
||||
|
||||
// Create MQTT message
|
||||
doc["print"]["sequence_id"] = "0";
|
||||
doc["print"]["command"] = "ams_filament_setting";
|
||||
doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
|
||||
@@ -250,26 +309,25 @@ bool setBambuSpool(String payload) {
|
||||
doc["print"]["nozzle_temp_min"] = minTemp;
|
||||
doc["print"]["nozzle_temp_max"] = maxTemp;
|
||||
doc["print"]["tray_type"] = type;
|
||||
//doc["print"]["cali_idx"] = (cali_idx != "") ? cali_idx : "";
|
||||
doc["print"]["tray_info_idx"] = tray_info_idx;
|
||||
doc["print"]["setting_id"] = setting_id;
|
||||
|
||||
// Serialize the JSON
|
||||
// Serialize and send MQTT message
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
Serial.println("Sending to Bambu printer:");
|
||||
Serial.println(output);
|
||||
|
||||
if (sendMqttMessage(output)) {
|
||||
Serial.println("Spool successfully set");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Failed to set spool");
|
||||
if (!sendMqttMessage(output)) {
|
||||
Serial.println("Failed to send filament settings to printer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.println("Filament settings sent successfully");
|
||||
doc.clear();
|
||||
yield();
|
||||
|
||||
// Send calibration if available
|
||||
if (cali_idx != "") {
|
||||
yield();
|
||||
doc["print"]["sequence_id"] = "0";
|
||||
@@ -278,21 +336,18 @@ bool setBambuSpool(String payload) {
|
||||
doc["print"]["nozzle_diameter"] = "0.4";
|
||||
doc["print"]["cali_idx"] = cali_idx.toInt();
|
||||
doc["print"]["tray_id"] = trayId < 200 ? trayId : 254;
|
||||
//doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
|
||||
|
||||
// Serialize the JSON
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
Serial.println("Sending calibration to printer:");
|
||||
Serial.println(output);
|
||||
|
||||
if (sendMqttMessage(output)) {
|
||||
Serial.println("Extrusion calibration successfully set");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Failed to set extrusion calibration");
|
||||
if (!sendMqttMessage(output)) {
|
||||
Serial.println("Failed to send calibration settings to printer");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.println("Calibration settings sent successfully");
|
||||
doc.clear();
|
||||
yield();
|
||||
}
|
||||
@@ -549,20 +604,47 @@ void reconnect() {
|
||||
uint8_t retries = 0;
|
||||
while (!client.connected()) {
|
||||
Serial.println("Attempting MQTT re/connection...");
|
||||
Serial.print("State before connect: ");
|
||||
Serial.println(client.state());
|
||||
bambu_connected = false;
|
||||
oledShowTopRow();
|
||||
|
||||
// Attempt to connect
|
||||
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) {
|
||||
Serial.println("MQTT re/connected");
|
||||
// Generate a random client ID suffix
|
||||
String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
|
||||
Serial.print("Reconnecting with client ID: ");
|
||||
Serial.println(clientId);
|
||||
|
||||
client.subscribe(report_topic.c_str());
|
||||
// Attempt to connect with clean session and will message
|
||||
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr)) {
|
||||
Serial.println("MQTT re/connected");
|
||||
|
||||
// Subscribe with QoS 1
|
||||
if (client.subscribe(report_topic.c_str(), 1)) {
|
||||
Serial.println("Successfully subscribed to topic with QoS 1: " + report_topic);
|
||||
} else {
|
||||
Serial.println("Failed to subscribe to topic: " + report_topic);
|
||||
}
|
||||
bambu_connected = true;
|
||||
oledShowTopRow();
|
||||
} else {
|
||||
int state = client.state();
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(client.state());
|
||||
Serial.println(" try again in 5 seconds");
|
||||
Serial.print(state);
|
||||
Serial.print(" (");
|
||||
// Print detailed error message
|
||||
switch(state) {
|
||||
case -4: Serial.print("MQTT_CONNECTION_TIMEOUT"); break;
|
||||
case -3: Serial.print("MQTT_CONNECTION_LOST"); break;
|
||||
case -2: Serial.print("MQTT_CONNECT_FAILED"); break;
|
||||
case -1: Serial.print("MQTT_DISCONNECTED"); break;
|
||||
case 1: Serial.print("MQTT_CONNECT_BAD_PROTOCOL"); break;
|
||||
case 2: Serial.print("MQTT_CONNECT_BAD_CLIENT_ID"); break;
|
||||
case 3: Serial.print("MQTT_CONNECT_UNAVAILABLE"); break;
|
||||
case 4: Serial.print("MQTT_CONNECT_BAD_CREDENTIALS"); break;
|
||||
case 5: Serial.print("MQTT_CONNECT_UNAUTHORIZED"); break;
|
||||
default: Serial.print("UNKNOWN"); break;
|
||||
}
|
||||
Serial.println(") try again in 5 seconds");
|
||||
bambu_connected = false;
|
||||
oledShowTopRow();
|
||||
|
||||
@@ -570,11 +652,9 @@ void reconnect() {
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
if (retries > 5) {
|
||||
Serial.println("Disable Bambu MQTT Task after 5 retries");
|
||||
//vTaskSuspend(BambuMqttTask);
|
||||
vTaskDelete(BambuMqttTask);
|
||||
break;
|
||||
}
|
||||
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
@@ -582,17 +662,32 @@ void reconnect() {
|
||||
|
||||
void mqtt_loop(void * parameter) {
|
||||
Serial.println("Bambu MQTT Task gestartet");
|
||||
unsigned long lastCheck = 0;
|
||||
|
||||
for(;;) {
|
||||
if (pauseBambuMqttTask) {
|
||||
vTaskDelay(10000);
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
if (!client.connected()) {
|
||||
Serial.println("Connection lost, attempting reconnect...");
|
||||
reconnect();
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
vTaskDelay(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Periodically check connection status
|
||||
if (now - lastCheck > 30000) { // Check every 30 seconds
|
||||
Serial.print("MQTT Status Check - Connected: ");
|
||||
Serial.println(client.connected() ? "Yes" : "No");
|
||||
lastCheck = now;
|
||||
}
|
||||
|
||||
client.loop();
|
||||
yield();
|
||||
esp_task_wdt_reset();
|
||||
@@ -617,15 +712,30 @@ bool setupMqtt() {
|
||||
sslClient.setInsecure();
|
||||
client.setServer(bambu_ip, 8883);
|
||||
|
||||
// Generate a random client ID suffix
|
||||
String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
|
||||
|
||||
// MQTT Connection Options
|
||||
client.setKeepAlive(60);
|
||||
client.setSocketTimeout(60); // Increase socket timeout
|
||||
|
||||
Serial.print("Connecting with client ID: ");
|
||||
Serial.println(clientId);
|
||||
|
||||
// Verbinden mit dem MQTT-Server
|
||||
bool connected = true;
|
||||
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode))
|
||||
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr))
|
||||
{
|
||||
client.setCallback(mqtt_callback);
|
||||
client.setBufferSize(5120);
|
||||
// Optional: Topic abonnieren
|
||||
client.subscribe(report_topic.c_str());
|
||||
//client.subscribe(request_topic.c_str());
|
||||
client.setBufferSize(16384); // Increased to 16KB to handle larger JSON
|
||||
|
||||
// Subscribe with QoS 1
|
||||
if (client.subscribe(report_topic.c_str(), 1)) {
|
||||
Serial.println("Successfully subscribed to topic with QoS 1: " + report_topic);
|
||||
} else {
|
||||
Serial.println("Failed to subscribe to topic: " + report_topic);
|
||||
}
|
||||
|
||||
Serial.println("MQTT-Client initialisiert");
|
||||
|
||||
oledShowMessage("Bambu Connected");
|
||||
@@ -635,7 +745,7 @@ bool setupMqtt() {
|
||||
xTaskCreatePinnedToCore(
|
||||
mqtt_loop, /* Function to implement the task */
|
||||
"BambuMqtt", /* Name of the task */
|
||||
8192, /* Stack size in words */
|
||||
16384, /* Stack size in words */
|
||||
NULL, /* Task input parameter */
|
||||
mqttTaskPrio, /* Priority of the task */
|
||||
&BambuMqttTask, /* Task handle. */
|
||||
|
||||
Reference in New Issue
Block a user