Compare commits

..

4 Commits

5 changed files with 138 additions and 322 deletions
-7
View File
@@ -1,7 +0,0 @@
.pio
.vscode
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
data
-10
View File
@@ -1,10 +0,0 @@
{
// 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"
]
}
+93 -149
View File
@@ -53,13 +53,7 @@ function initWebSocket() {
// Wenn eine existierende Verbindung besteht, diese erst schließen
if (socket) {
try {
socket.onclose = null; // Remove onclose handler before closing
socket.onerror = null; // Remove error handler
socket.close();
} catch (e) {
console.error('Error closing existing socket:', e);
}
socket.close();
socket = null;
}
@@ -67,80 +61,110 @@ function initWebSocket() {
socket = new WebSocket('ws://' + window.location.host + '/ws');
socket.onopen = function() {
console.log('WebSocket connection established');
isConnected = true;
updateConnectionStatus();
startHeartbeat(); // Starte Heartbeat nach erfolgreicher Verbindung
};
socket.onclose = function(event) {
console.log('WebSocket connection closed:', event.code, event.reason);
socket.onclose = function() {
isConnected = false;
updateConnectionStatus();
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
}
if (heartbeatTimer) clearInterval(heartbeatTimer);
// Nur neue Verbindung versuchen, wenn kein Timer läuft
if (!reconnectTimer) {
reconnectTimer = setTimeout(() => {
console.log('Attempting to reconnect...');
initWebSocket();
}, RECONNECT_INTERVAL);
}
};
socket.onerror = function(error) {
console.error('WebSocket error occurred:', error);
isConnected = false;
updateConnectionStatus();
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
if (heartbeatTimer) clearInterval(heartbeatTimer);
// Bei Fehler Verbindung schließen und neu aufbauen
if (socket) {
socket.close();
socket = null;
}
};
socket.onmessage = function(event) {
try {
lastHeartbeatResponse = Date.now();
const data = JSON.parse(event.data);
lastHeartbeatResponse = Date.now(); // Aktualisiere Zeitstempel bei jeder Server-Antwort
const data = JSON.parse(event.data);
if (data.type === 'amsData') {
displayAmsData(data.payload);
} else if (data.type === 'nfcTag') {
updateNfcStatusIndicator(data.payload);
} else if (data.type === 'nfcData') {
updateNfcData(data.payload);
} else if (data.type === 'writeNfcTag') {
handleWriteNfcTagResponse(data.success);
} else if (data.type === 'heartbeat') {
// Optional: Spezifische Behandlung von Heartbeat-Antworten
// Update status dots
const bambuDot = document.getElementById('bambuDot');
const spoolmanDot = document.getElementById('spoolmanDot');
const ramStatus = document.getElementById('ramStatus');
// Handle different message types
switch(data.type) {
case 'amsData':
displayAmsData(data.payload);
break;
case 'nfcTag':
updateNfcStatusIndicator(data.payload);
break;
case 'nfcData':
updateNfcData(data.payload);
break;
case 'writeNfcTag':
handleWriteNfcTagResponse(data.success);
break;
case 'heartbeat':
handleHeartbeatResponse(data);
break;
case 'setSpoolmanSettings':
handleSpoolmanSettingsResponse(data);
break;
default:
console.warn('Unknown message type:', data.type);
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() {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
type: 'reconnect',
payload: 'bambu'
}));
}
};
} else {
bambuDot.style.cursor = 'default';
bambuDot.onclick = null;
}
}
if (spoolmanDot) {
spoolmanDot.className = 'status-dot ' + (data.spoolman_connected ? 'online' : 'offline');
// Add click handler only when offline
if (!data.spoolman_connected) {
spoolmanDot.style.cursor = 'pointer';
spoolmanDot.onclick = function() {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
type: 'reconnect',
payload: 'spoolman'
}));
}
};
} else {
spoolmanDot.style.cursor = 'default';
spoolmanDot.onclick = null;
}
}
if (ramStatus) {
ramStatus.textContent = `${data.freeHeap}k`;
}
}
else if (data.type === 'setSpoolmanSettings') {
if (data.payload == 'success') {
showNotification(`Spoolman Settings set successfully`, true);
} else {
showNotification(`Error setting Spoolman Settings`, false);
}
} catch (error) {
console.error('Error processing WebSocket message:', error);
}
};
} catch (error) {
console.error('Error initializing WebSocket:', error);
isConnected = false;
updateConnectionStatus();
// Nur neue Verbindung versuchen, wenn kein Timer läuft
if (!reconnectTimer) {
reconnectTimer = setTimeout(() => {
console.log('Attempting to reconnect after error...');
initWebSocket();
}, RECONNECT_INTERVAL);
}
@@ -365,7 +389,6 @@ function updateSpoolButtons(show) {
});
}
// Verbesserte Funktion zum Behandeln von Spoolman Settings
function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp_min, nozzle_temp_max) {
// Hole das ausgewählte Filament
const selectedText = document.getElementById("selected-filament").textContent;
@@ -396,7 +419,6 @@ function handleSpoolmanSettings(tray_info_idx, setting_id, cali_idx, nozzle_temp
}
}
// Verbesserte Funktion zum Behandeln von Spool Out
function handleSpoolOut() {
// Erstelle Payload
const payload = {
@@ -421,21 +443,17 @@ function handleSpoolOut() {
}
}
// Verbesserte Funktion zum Behandeln des Spool-In-Klicks
// Neue Funktion zum Behandeln des Spool-In-Klicks
function handleSpoolIn(amsId, trayId) {
console.log("handleSpoolIn called with amsId:", amsId, "trayId:", trayId);
// Prüfe WebSocket Verbindung zuerst
if (!socket || socket.readyState !== WebSocket.OPEN) {
showNotification("No active WebSocket connection!", false);
console.error("WebSocket not connected, state:", socket ? socket.readyState : "no socket");
console.error("WebSocket not connected");
return;
}
// Hole das ausgewählte Filament
const selectedText = document.getElementById("selected-filament").textContent;
console.log("Selected filament:", selectedText);
if (selectedText === "Please choose...") {
showNotification("Choose Filament first", false);
return;
@@ -448,55 +466,53 @@ function handleSpoolIn(amsId, trayId) {
if (!selectedSpool) {
showNotification("Selected Spool not found", false);
console.error("Selected spool not found in spoolsData");
return;
}
console.log("Found spool data:", selectedSpool);
// Temperaturwerte extrahieren
let minTemp = "175";
let maxTemp = "275";
if (selectedSpool.filament &&
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];
}
// Extrahiere bambu_idx
let bambuIdx = "GFL99"; // Default zu Generic PLA
if (selectedSpool.filament?.extra?.bambu_idx) {
bambuIdx = selectedSpool.filament.extra.bambu_idx.replace(/['"]/g, '');
} else if (selectedSpool.extra?.bambu_idx) {
bambuIdx = selectedSpool.extra.bambu_idx.replace(/['"]/g, '');
}
// Erstelle Payload
const payload = {
type: 'setBambuSpool',
payload: {
amsId: amsId,
trayId: trayId,
color: selectedSpool.filament && selectedSpool.filament.color_hex ? selectedSpool.filament.color_hex : "FFFFFF",
color: selectedSpool.filament.color_hex || "FFFFFF",
nozzle_temp_min: parseInt(minTemp),
nozzle_temp_max: parseInt(maxTemp),
type: selectedSpool.filament && selectedSpool.filament.material ? selectedSpool.filament.material : "PLA",
brand: selectedSpool.filament && selectedSpool.filament.vendor ? selectedSpool.filament.vendor.name : "",
tray_info_idx: bambuIdx,
type: selectedSpool.filament.material,
brand: selectedSpool.filament.vendor.name,
tray_info_idx: selectedSpool.filament.extra.bambu_idx.replace(/['"]+/g, '').trim(),
cali_idx: "-1" // Default-Wert setzen
}
};
console.log("Sending payload:", payload);
// Prüfe, ob der Key cali_idx vorhanden ist und setze ihn
if (selectedSpool.filament.extra.bambu_cali_id) {
payload.payload.cali_idx = selectedSpool.filament.extra.bambu_cali_id.replace(/['"]+/g, '').trim();
}
// Prüfe, ob der Key bambu_setting_id vorhanden ist
if (selectedSpool.filament.extra.bambu_setting_id) {
payload.payload.bambu_setting_id = selectedSpool.filament.extra.bambu_setting_id.replace(/['"]+/g, '').trim();
}
console.log("Spool-In Payload:", payload);
try {
socket.send(JSON.stringify(payload));
showNotification(`Spool settings sent to printer. Please wait...`, true);
showNotification(`Spool set in AMS ${amsId} Tray ${trayId}. Pls wait`, true);
} catch (error) {
console.error("Error sending WebSocket message:", error);
showNotification("Error sending spool settings!", false);
console.error("Fehler beim Senden der WebSocket Nachricht:", error);
showNotification("Error while sending", false);
}
}
@@ -671,75 +687,3 @@ function showNotification(message, isSuccess) {
}, 300);
}, 3000);
}
// Neue Handler-Funktionen für bessere Modularität
function handleHeartbeatResponse(data) {
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');
if (!data.bambu_connected) {
bambuDot.style.cursor = 'pointer';
bambuDot.onclick = function() {
sendReconnectRequest('bambu');
};
} else {
bambuDot.style.cursor = 'default';
bambuDot.onclick = null;
}
}
if (spoolmanDot) {
spoolmanDot.className = 'status-dot ' + (data.spoolman_connected ? 'online' : 'offline');
if (!data.spoolman_connected) {
spoolmanDot.style.cursor = 'pointer';
spoolmanDot.onclick = function() {
sendReconnectRequest('spoolman');
};
} else {
spoolmanDot.style.cursor = 'default';
spoolmanDot.onclick = null;
}
}
if (ramStatus) {
ramStatus.textContent = `${data.freeHeap}k`;
}
}
function handleSpoolmanSettingsResponse(data) {
if (data.payload === 'success') {
showNotification(`Spoolman Settings set successfully`, true);
} else {
showNotification(`Error setting Spoolman Settings`, false);
}
}
function sendReconnectRequest(target) {
if (socket?.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
type: 'reconnect',
payload: target
}));
}
}
// Verbesserte Funktion zum Senden von WebSocket-Nachrichten
function sendWebSocketMessage(message) {
if (!socket || socket.readyState !== WebSocket.OPEN) {
console.error('WebSocket is not connected');
showNotification("Connection error - please try again", false);
return;
}
try {
const jsonString = JSON.stringify(message);
console.log('Sending WebSocket message:', jsonString);
socket.send(jsonString);
} catch (error) {
console.error('Error sending WebSocket message:', error);
showNotification("Error sending message", false);
}
}
+44 -155
View File
@@ -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,69 +197,18 @@ FilamentResult findFilamentIdx(String brand, String type) {
}
bool sendMqttMessage(const String& payload) {
// 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()));
// 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");
Serial.println("Sending MQTT message");
Serial.println(payload);
if (client.publish(report_topic.c_str(), payload.c_str()))
{
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;
}
return false;
}
bool setBambuSpool(String payload) {
Serial.println("Spool settings received from WebSocket");
Serial.println("Spool settings in");
Serial.println(payload);
// Parse the JSON
@@ -271,28 +220,21 @@ bool setBambuSpool(String payload) {
return false;
}
// 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>();
int amsId = doc["amsId"];
int trayId = doc["trayId"];
String color = doc["color"].as<String>();
color.toUpperCase();
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
int minTemp = doc["nozzle_temp_min"];
int maxTemp = doc["nozzle_temp_max"];
String type = doc["type"].as<String>();
(type == "PLA+") ? type = "PLA" : type;
String brand = doc["brand"] | ""; // Default to empty string if not present
String brand = doc["brand"].as<String>();
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>();
@@ -300,7 +242,6 @@ 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;
@@ -309,25 +250,26 @@ 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 and send MQTT message
// Serialize the JSON
String output;
serializeJson(doc, output);
Serial.println("Sending to Bambu printer:");
Serial.println(output);
if (!sendMqttMessage(output)) {
Serial.println("Failed to send filament settings to printer");
if (sendMqttMessage(output)) {
Serial.println("Spool successfully set");
}
else
{
Serial.println("Failed to set spool");
return false;
}
Serial.println("Filament settings sent successfully");
doc.clear();
yield();
// Send calibration if available
if (cali_idx != "") {
yield();
doc["print"]["sequence_id"] = "0";
@@ -336,18 +278,21 @@ 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("Failed to send calibration settings to printer");
if (sendMqttMessage(output)) {
Serial.println("Extrusion calibration successfully set");
}
else
{
Serial.println("Failed to set extrusion calibration");
return false;
}
Serial.println("Calibration settings sent successfully");
doc.clear();
yield();
}
@@ -604,47 +549,20 @@ 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();
// Generate a random client ID suffix
String clientId = String(bambu_serialnr) + "_" + String(random(0xffff), HEX);
Serial.print("Reconnecting with client ID: ");
Serial.println(clientId);
// Attempt to connect with clean session and will message
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr)) {
// Attempt to connect
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) {
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);
}
client.subscribe(report_topic.c_str());
bambu_connected = true;
oledShowTopRow();
} else {
int state = client.state();
Serial.print("failed, rc=");
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");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
bambu_connected = false;
oledShowTopRow();
@@ -652,9 +570,11 @@ 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++;
}
}
@@ -662,32 +582,17 @@ 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();
@@ -712,30 +617,14 @@ 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(clientId.c_str(), bambu_username, bambu_accesscode, nullptr, 0, true, nullptr))
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode))
{
client.setCallback(mqtt_callback);
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);
}
client.setBufferSize(5120);
client.subscribe(report_topic.c_str());
//client.subscribe(request_topic.c_str());
Serial.println("MQTT-Client initialisiert");
oledShowMessage("Bambu Connected");
@@ -745,7 +634,7 @@ bool setupMqtt() {
xTaskCreatePinnedToCore(
mqtt_loop, /* Function to implement the task */
"BambuMqtt", /* Name of the task */
16384, /* Stack size in words */
10240, /* Stack size in words */
NULL, /* Task input parameter */
mqttTaskPrio, /* Priority of the task */
&BambuMqttTask, /* Task handle. */
+1 -1
View File
@@ -59,7 +59,7 @@ void initWiFi() {
if(wm_nonblocking) wm.setConfigPortalBlocking(false);
//wm.setConfigPortalTimeout(320); // Portal nach 5min schließen
wm.setWiFiAutoReconnect(true);
wm.setConnectTimeout(5);
wm.setConnectTimeout(10);
oledShowTopRow();
oledShowMessage("WiFi Setup");