Compare commits

...

22 Commits

Author SHA1 Message Date
Manuel Weiser 61dadc0aa1 fix: comment out combine_binaries target in PlatformIO configuration 2025-02-14 10:50:04 +01:00
Manuel Weiser a2ee9a98a6 fix: update Bambu spool settings and connection handling logic 2025-02-14 10:35:47 +01:00
Manuel Weiser ae96c729b1 Corrected Amazon Links in Readme 2025-02-13 21:40:41 +01:00
Manuel Weiser 1d337dd990 add PlatformIO configuration file for ESP32 development with custom build targets 2025-02-13 20:59:07 +01:00
Manuel Weiser cfa6c12d65 add script to combine firmware and SPIFFS binaries, and remove unused display files 2025-02-13 18:42:34 +01:00
Manuel Weiser b2aeae5815 add link to image folder and FilaMan website in README for additional resources 2025-02-13 17:39:41 +01:00
Manuel Weiser 5cdbc5f2cc add new image assets for enhanced visual content 2025-02-13 17:37:17 +01:00
Manuel Weiser 7a5415ad80 add FilaMan website link to README for additional resource 2025-02-13 16:57:07 +01:00
Manuel Weiser 36fd271d0c remove unused image files and update scale image for clarity 2025-02-13 16:24:49 +01:00
Manuel Weiser a39e199c79 update "Buy Me A Coffee" button size in README for better visibility 2025-02-13 16:22:52 +01:00
Manuel Weiser b7004f5b71 add scale image to README for enhanced visual representation 2025-02-13 16:19:42 +01:00
Manuel Weiser e5bf05ee43 refactor header and RFID display logic, add spool out functionality, and update image assets 2025-02-13 16:13:46 +01:00
Manuel Weiser f01a42d850 implement AMS data sending every minute and remove unused CSS file 2025-02-13 13:38:15 +01:00
Manuel Weiser 04101d6cfa add new stylesheet for enhanced UI and update WebSocket handling for NFC tag detection 2025-02-13 13:12:29 +01:00
Manuel Weiser d46d5e14d2 update navigation links, enhance NFC data display, and improve MQTT connection handling 2025-02-13 12:59:43 +01:00
Manuel Weiser be0a23e736 remove unused headings from RFID interface and update tray display styles 2025-02-13 12:20:29 +01:00
Manuel Weiser c46553434b add .gitkeep file to data directory for tracking empty folders 2025-02-13 09:30:10 +01:00
Manuel Weiser 89a88463ef refactor CSS styles for reboot button and adjust input width in bambu settings 2025-02-13 09:25:03 +01:00
Manuel Weiser 3e8e19e8dc refactor Bambu credentials handling and improve input validation 2025-02-13 09:20:02 +01:00
Manuel Weiser d220479709 update copyright year in LICENSE file to 2025 2025-02-13 08:29:19 +01:00
Manuel Weiser f49bbdb664 add LICENSE file and update README for clarity and additional support information 2025-02-13 08:29:02 +01:00
Manuel Weiser 412127fce5 add PN532 pin configuration details to README 2025-02-13 07:19:46 +01:00
34 changed files with 551 additions and 248 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Manuel Weiser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+61 -66
View File
@@ -1,10 +1,14 @@
# FilaMan - Filament Management System # FilaMan - Filament Management System
A comprehensive filament management system combining ESP32-based hardware for weight measurement and NFC tag reading/writing with a web interface for managing filament spools in conjunction with Bambu Lab AMS and Spoolman. 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.
## Project Overview ![Scale](./img/scale_trans.png)
FilaMan is designed to streamline the management of filament spools for 3D printing. The system consists of an ESP32 microcontroller that handles weight measurement and NFC tag operations, and a web interface that allows users to manage filament spools, monitor AMS (Automatic Material System) status, and interact with Spoolman and Bambu Lab printers.
More Images can be found in the [img Folder](/img/) or my website:
[FilaMan Website](https://www.filaman.app)
### ESP32 Hardware Features ### ESP32 Hardware Features
- **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking. - **Weight Measurement:** Using a load cell with HX711 amplifier for precise weight tracking.
@@ -12,13 +16,15 @@ FilaMan is designed to streamline the management of filament spools for 3D print
- **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.
- **Data Persistence:** Stores calibration data in EEPROM. - **NFC-Tag NTAG215:** Use NTAG215 because of enaught space on the Tag
- **Watchdog Timer:** Ensures system stability.
### Web Interface Features ### Web Interface Features
- **Real-time Updates:** WebSocket connection for live data updates. - **Real-time Updates:** WebSocket connection for live data updates.
- **NFC Tag Management:** Write filament data to NFC tags. - **NFC Tag Management:**
- **AMS Integration:** - 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. - Display current AMS tray contents.
- Assign filaments to AMS slots. - Assign filaments to AMS slots.
- Support for external spool holder. - Support for external spool holder.
@@ -28,6 +34,9 @@ FilaMan is designed to streamline the management of filament spools for 3D print
- Update spool weights automatically. - Update spool weights automatically.
- Track NFC tag assignments. - Track NFC tag assignments.
### 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>
## Detailed Functionality ## Detailed Functionality
### ESP32 Functionality ### ESP32 Functionality
@@ -39,13 +48,52 @@ FilaMan is designed to streamline the management of filament spools for 3D print
- **User Interactions:** The web interface allows users to interact with the system, select filaments, write NFC tags, and monitor AMS status. - **User Interactions:** The web interface allows users to interact with the system, select filaments, write NFC tags, and monitor AMS status.
- **UI Elements:** Includes dropdowns for selecting manufacturers and filaments, buttons for writing NFC tags, and real-time status indicators. - **UI Elements:** Includes dropdowns for selecting manufacturers and filaments, buttons for writing NFC tags, and real-time status indicators.
## Hardware Requirements
### Components
- **ESP32 Development Board:** Any ESP32 variant.
[Amazon Link](https://amzn.eu/d/aXThslf)
- **HX711 Load Cell Amplifier:** For weight measurement.
[Amazon Link](https://amzn.eu/d/1wZ4v0x)
- **OLED Display:** 128x64 SSD1306.
[Amazon Link](https://amzn.eu/d/dozAYDU)
- **PN532 NFC Module:** For NFC tag operations.
[Amazon Link](https://amzn.eu/d/8205DDh)
- **NFC-Tag:** NTAG215
[Amazon Link](https://amzn.eu/d/fywy4c4)
### Pin Configuration
| Component | ESP32 Pin |
|-------------------|-----------|
| HX711 DOUT | 16 |
| HX711 SCK | 17 |
| OLED SDA | 21 |
| OLED SCL | 22 |
| PN532 IRQ | 32 |
| PN532 RESET | 33 |
| PN532 SCK | 14 |
| PN532 MOSI | 13 |
| PN532 MISO | 12 |
| PN532 CS/SS | 15 |
## Software Dependencies
### ESP32 Libraries
- `WiFiManager`: Network configuration
- `ESPAsyncWebServer`: Web server functionality
- `ArduinoJson`: JSON parsing and creation
- `PubSubClient`: MQTT communication
- `Adafruit_PN532`: NFC functionality
- `Adafruit_SSD1306`: OLED display control
- `HX711`: Load cell communication
## Installation ## Installation
### Prerequisites ### Prerequisites
- **Software:** - **Software:**
- [PlatformIO](https://platformio.org/) in VS Code - [PlatformIO](https://platformio.org/) in VS Code
- [Spoolman](https://github.com/Donkie/Spoolman) instance - [Spoolman](https://github.com/Donkie/Spoolman) instance
- Bambu Lab printer (optional for AMS integration)
- **Hardware:** - **Hardware:**
- ESP32 Development Board - ESP32 Development Board
- HX711 Load Cell Amplifier - HX711 Load Cell Amplifier
@@ -57,7 +105,7 @@ FilaMan is designed to streamline the management of filament spools for 3D print
### Step-by-Step Installation ### Step-by-Step Installation
1. **Clone the Repository:** 1. **Clone the Repository:**
```bash ```bash
git clone https://github.com/yourusername/FilaMan.git git clone https://github.com/ManuelW77/Filaman.git
cd FilaMan cd FilaMan
``` ```
2. **Install Dependencies:** 2. **Install Dependencies:**
@@ -73,62 +121,6 @@ FilaMan is designed to streamline the management of filament spools for 3D print
- Configure WiFi settings through the captive portal. - Configure WiFi settings through the captive portal.
- Access the web interface at `http://filaman.local` or the IP address. - Access the web interface at `http://filaman.local` or the IP address.
## Hardware Requirements
### Components
- **ESP32 Development Board:** Any ESP32 variant.
- **HX711 Load Cell Amplifier:** For weight measurement.
- **Load Cell:** Weight sensor.
- **OLED Display:** 128x64 SSD1306.
- **PN532 NFC Module:** For NFC tag operations.
- **Connecting Wires:** For connections.
### Pin Configuration
| Component | ESP32 Pin |
|-------------------|-----------|
| HX711 DOUT | 16 |
| HX711 SCK | 17 |
| OLED SDA | 21 |
| OLED SCL | 22 |
| PN532 IRQ | 32 |
| PN532 RESET | 33 |
## Software Dependencies
### ESP32 Libraries
- `WiFiManager`: Network configuration
- `ESPAsyncWebServer`: Web server functionality
- `ArduinoJson`: JSON parsing and creation
- `PubSubClient`: MQTT communication
- `Adafruit_PN532`: NFC functionality
- `Adafruit_SSD1306`: OLED display control
- `HX711`: Load cell communication
### External Services
- **Bambu Lab Printer:** For AMS integration.
- **Spoolman:** For filament management.
## API Communication
### Spoolman Integration
The system communicates with Spoolman using its REST API for:
- Fetching spool information.
- Updating spool weights.
- Managing NFC tag assignments.
### Data Format
```json
{
"version": "2.0",
"protocol": "openspool",
"color_hex": "FFFFFF",
"type": "PLA",
"min_temp": 200,
"max_temp": 220,
"brand": "Vendor",
"sm_id": "1234"
}
```
## Documentation ## Documentation
@@ -159,4 +151,7 @@ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
## Availability ## Availability
The code can be tested and the application can be downloaded from the [GitHub repository](https://github.com/yourusername/FilaMan). 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>
+68
View File
@@ -0,0 +1,68 @@
Import("env")
from os.path import join, exists
import shutil
import os
def combine_binaries(source, target, env):
# Define paths for binary files
build_dir = env.subst("$BUILD_DIR")
project_dir = env.subst("$PROJECT_DIR")
combined_bin = join(build_dir, "combined.bin")
firmware_bin = join(build_dir, "firmware.bin")
spiffs_bin = join(build_dir, "spiffs.bin")
# Define target firmware path
firmware_dir = join(project_dir, "firmware")
target_firmware = join(firmware_dir, "filaman.bin")
# Build firmware if it doesn't exist
if not exists(firmware_bin):
print("Building firmware...")
env.Execute("pio run -t buildprog")
# Build SPIFFS if it doesn't exist
if not exists(spiffs_bin):
print("Building SPIFFS image...")
env.Execute("pio run -t buildfs")
# Check if files exist after build attempts
if not exists(firmware_bin):
raise Exception("Firmware binary not found at: " + firmware_bin)
if not exists(spiffs_bin):
raise Exception("SPIFFS binary not found at: " + spiffs_bin)
print("Found firmware at:", firmware_bin)
print("Found SPIFFS at:", spiffs_bin)
# Command to merge firmware and SPIFFS
merge_command = (
"esptool.py --chip esp32 merge_bin -o {combined_bin} "
"--flash_mode dio --flash_freq 40m --flash_size 4MB "
"0x10000 {firmware_bin} 0x310000 {spiffs_bin}"
).format(
combined_bin=combined_bin,
firmware_bin=firmware_bin,
spiffs_bin=spiffs_bin
)
print("Executing merge command:", merge_command)
env.Execute(merge_command)
# Create firmware directory if it doesn't exist
if not exists(firmware_dir):
os.makedirs(firmware_dir)
# Move combined binary to target location
print(f"Moving combined binary to {target_firmware}")
shutil.copy2(combined_bin, target_firmware)
print(f"Binary successfully moved to {target_firmware}")
# Register the custom target with explicit dependencies
env.AddCustomTarget(
name="combine_binaries",
dependencies=["buildfs", "buildprog"],
actions=[combine_binaries],
title="Combine Firmware & SPIFFS",
description="Combines firmware.bin and spiffs.bin into a single binary"
)
View File
View File
+13 -30
View File
@@ -6,39 +6,22 @@
<title>FilaMan - Filament Management Tool</title> <title>FilaMan - Filament Management Tool</title>
<link rel="icon" type="image/png" href="/favicon.ico"> <link rel="icon" type="image/png" href="/favicon.ico">
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<style>
.status-container {
float: right;
display: flex;
gap: 10px;
align-items: center;
margin-right: 10px;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
margin-right: 4px;
}
.status-item {
display: flex;
align-items: center;
font-size: 0.8em;
color: #fff;
}
.online { background-color: #2ecc71; }
.offline { background-color: #e74c3c; }
.ram-status { color: #fff; font-size: 0.8em; }
</style>
</head> </head>
<body> <body>
<div class="navbar"> <div class="navbar">
<img src="/logo.png" alt="FilaMan Logo" class="logo"> <div style="display: flex; align-items: center; gap: 2rem;">
<a href="/">Start</a> <img src="/logo.png" alt="FilaMan Logo" class="logo">
<a href="/waage">Scale</a> <div class="logo-text">
<a href="/spoolman">Spoolman/Bambu</a> <h1>FilaMan</h1>
<a href="/about">About</a> <h4>Filament Management Tool</h4>
</div>
</div>
<nav style="display: flex; gap: 1rem;">
<a href="/">Start</a>
<a href="/waage">Scale</a>
<a href="/spoolman">Spoolman/Bambu</a>
<a href="/">About</a>
</nav>
<div class="status-container"> <div class="status-container">
<div class="status-item"> <div class="status-item">
<span class="status-dot" id="bambuDot"></span>B <span class="status-dot" id="bambuDot"></span>B
-2
View File
@@ -64,7 +64,6 @@
<div class="column"> <div class="column">
<div class="feature-box"> <div class="feature-box">
<h2>Spoolman Spools</h2> <h2>Spoolman Spools</h2>
<h2>1. select Manufacturer</h2>
<label for="vendorSelect">Manufacturer:</label> <label for="vendorSelect">Manufacturer:</label>
<div style="display: flex; justify-content: space-between; align-items: center;"> <div style="display: flex; justify-content: space-between; align-items: center;">
<select id="vendorSelect" class="styled-select"> <select id="vendorSelect" class="styled-select">
@@ -78,7 +77,6 @@
</div> </div>
<div id="filamentSection" class="feature-box hidden"> <div id="filamentSection" class="feature-box hidden">
<h2>2. Select Spool</h2>
<label>Spool / Filament:</label> <label>Spool / Filament:</label>
<div class="custom-dropdown"> <div class="custom-dropdown">
<div class="dropdown-button" onclick="toggleFilamentDropdown()"> <div class="dropdown-button" onclick="toggleFilamentDropdown()">
+57 -20
View File
@@ -244,23 +244,38 @@ function displayAmsData(amsData) {
tray[field] !== 'null' tray[field] !== 'null'
); );
if (!hasAnyContent) { // Bestimme den Anzeigenamen für das Tray
return ` const trayDisplayName = (ams.ams_id === 255) ? 'External' : `Tray ${tray.id}`;
<div class="tray">
<p><b>Tray ${tray.id}</b></p>
<p>Empty</p>
</div>
<hr>`;
}
// Nur für nicht-leere Trays den Button-HTML erstellen // Nur für nicht-leere Trays den Button-HTML erstellen
const buttonHtml = ` const buttonHtml = `
<button class="spool-button" onclick="handleSpoolIn(${ams.ams_id}, ${tray.id})" <button class="spool-button" onclick="handleSpoolIn(${ams.ams_id}, ${tray.id})"
style="position: absolute; top: 5px; left: 5px; style="position: absolute; top: -30px; left: -15px;
background: none; border: none; padding: 0; background: none; border: none; padding: 0;
cursor: pointer; display: none;"> cursor: pointer; display: none;">
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;"> <img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px;">
</button>`; </button>`;
// 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;
background: none; border: none; padding: 0;
cursor: pointer; display: block;">
<img src="spool_in.png" alt="Spool In" style="width: 48px; height: 48px; transform: rotate(180deg) scaleX(-1);">
</button>`;
if (!hasAnyContent) {
return `
<div class="tray">
<p class="tray-head">${trayDisplayName}</p>
<p>
${(ams.ams_id === 255 && tray.tray_type === '') ? buttonHtml : ''}
Empty
</p>
</div>
<hr>`;
}
// Generiere den Type mit Color-Box zusammen // Generiere den Type mit Color-Box zusammen
const typeWithColor = tray.tray_type ? const typeWithColor = tray.tray_type ?
@@ -297,20 +312,18 @@ function displayAmsData(amsData) {
? `<p>Nozzle Temp: ${tray.nozzle_temp_min}°C - ${tray.nozzle_temp_max}°C</p>` ? `<p>Nozzle Temp: ${tray.nozzle_temp_min}°C - ${tray.nozzle_temp_max}°C</p>`
: ''; : '';
// Bestimme den Anzeigenamen für das Tray
const trayDisplayName = (ams.ams_id === 255) ? 'External' : `Tray ${tray.id}`;
return ` return `
<div class="tray" ${tray.tray_color ? `style="border-left: 4px solid #${tray.tray_color};"` : 'style="border-left: 4px solid #007bff;"'}> <div class="tray" ${tray.tray_color ? `style="border-left: 4px solid #${tray.tray_color};"` : 'style="border-left: 4px solid #007bff;"'}>
<div style="position: relative;"> <div style="position: relative;">
${buttonHtml} ${buttonHtml}
<p><b>${trayDisplayName}</b></p> <p class="tray-head">${trayDisplayName}</p>
${typeWithColor} ${typeWithColor}
${trayDetails} ${trayDetails}
${tempHTML} ${tempHTML}
${(ams.ams_id === 255 && tray.tray_type !== '') ? outButtonHtml : ''}
</div> </div>
</div>
<hr>`; </div>`;
}).join(''); }).join('');
const amsInfo = ` const amsInfo = `
@@ -333,6 +346,30 @@ function updateSpoolButtons(show) {
}); });
} }
function handleSpoolOut() {
// Erstelle Payload
const payload = {
type: 'setBambuSpool',
payload: {
amsId: 255,
trayId: 254,
color: "FFFFFF",
nozzle_temp_min: 0,
nozzle_temp_max: 0,
type: "",
brand: ""
}
};
try {
socket.send(JSON.stringify(payload));
showNotification(`External Spool removed. Pls wait`, true);
} catch (error) {
console.error("Fehler beim Senden der WebSocket Nachricht:", error);
showNotification("Error while sending!", false);
}
}
// Neue Funktion zum Behandeln des Spool-In-Klicks // Neue Funktion zum Behandeln des Spool-In-Klicks
function handleSpoolIn(amsId, trayId) { function handleSpoolIn(amsId, trayId) {
// Prüfe WebSocket Verbindung zuerst // Prüfe WebSocket Verbindung zuerst
@@ -362,13 +399,13 @@ function handleSpoolIn(amsId, trayId) {
// Temperaturwerte extrahieren // Temperaturwerte extrahieren
let minTemp = "175"; let minTemp = "175";
let maxTemp = "275"; let maxTemp = "275";
if (Array.isArray(selectedSpool.filament.nozzle_temperature) && if (Array.isArray(selectedSpool.filament.nozzle_temperature) &&
selectedSpool.filament.nozzle_temperature.length >= 2) { selectedSpool.filament.nozzle_temperature.length >= 2) {
minTemp = selectedSpool.filament.nozzle_temperature[0]; minTemp = selectedSpool.filament.nozzle_temperature[0];
maxTemp = selectedSpool.filament.nozzle_temperature[1]; maxTemp = selectedSpool.filament.nozzle_temperature[1];
} }
// Erstelle Payload // Erstelle Payload
const payload = { const payload = {
type: 'setBambuSpool', type: 'setBambuSpool',
@@ -391,7 +428,7 @@ function handleSpoolIn(amsId, trayId) {
showNotification(`Spool set in AMS ${amsId} Tray ${trayId}. Pls wait`, true); showNotification(`Spool set in AMS ${amsId} Tray ${trayId}. Pls wait`, true);
} catch (error) { } catch (error) {
console.error("Fehler beim Senden der WebSocket Nachricht:", error); console.error("Fehler beim Senden der WebSocket Nachricht:", error);
showNotification("Fehler beim Senden der Daten", false); showNotification("Error while sending", false);
} }
} }
@@ -449,7 +486,7 @@ function updateNfcData(data) {
// HTML für die Datenanzeige erstellen // HTML für die Datenanzeige erstellen
let html = ` let html = `
<div style="margin-top: 10px;"> <div class="nfc-card-data" style="margin-top: 10px;">
<p><strong>Brand:</strong> ${data.brand || 'N/A'}</p> <p><strong>Brand:</strong> ${data.brand || 'N/A'}</p>
<p><strong>Type:</strong> ${data.type || 'N/A'} ${data.color_hex ? `<span style=" <p><strong>Type:</strong> ${data.type || 'N/A'} ${data.color_hex ? `<span style="
background-color: #${data.color_hex}; background-color: #${data.color_hex};
@@ -567,4 +604,4 @@ function showNotification(message, isSuccess) {
notification.remove(); notification.remove();
}, 300); }, 300);
}, 3000); }, 3000);
} }
-6
View File
@@ -32,12 +32,6 @@
.then(data => { .then(data => {
if (data.healthy) { if (data.healthy) {
document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!'; document.getElementById('bambuStatusMessage').innerText = 'Bambu Credentials saved!';
// Erstelle und zeige den Reboot-Button
const rebootBtn = document.createElement('button');
rebootBtn.innerText = 'Reboot now';
rebootBtn.className = 'reboot-button';
rebootBtn.onclick = () => window.location.href = '/reboot';
document.getElementById('bambuStatusMessage').appendChild(rebootBtn);
} else { } else {
document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.'; document.getElementById('bambuStatusMessage').innerText = 'Error while saving Bambu Credentials.';
} }
+186 -91
View File
@@ -1,53 +1,144 @@
/* Allgemeine Stile */ /* Allgemeine Stile */
:root {
--primary-color: #63bb67; /* Hauptfarbe Grün */
--primary-light: #8cd590; /* Helleres Grün */
--background-green: #28902D; /* Dunkleres Hintergrund-Grün */
--text-color: #1e293b; /* Dunkelgrau für Text */
--inner-box-bg: #63bb67; /* Grüner Hintergrund für innere Boxen */
--inner-text-color: #ffffff; /* Weißer Text in Boxen */
--stat-value-color: #ffd700; /* Gelber Text für Werte */
--header-bg: #28902D; /* Hintergrundfarbe für den Header */
--header-border: #f7208c; /* Pinke Randfarbe für den Header */
--accent-color: #d51274; /* Pink für Akzentfarben */
--header-text: #40e8b7; /* Türkisfarbener Text */
--background-light: #63bb67; /* Helleres Grün für Container */
--border-color: rgba(255, 255, 255, 0.1); /* Subtile Borders */
--card-background: rgba(255, 255, 255, 0.15);
--card-shadow: 0 4px 15px rgba(31, 41, 55, 0.1);
--glass-bg: rgba(255, 255, 255, 0.15);
--glass-border: 1px solid rgba(255, 255, 255, 0.2);
--glass-blur: blur(12px);
--glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
body { body {
font-family: Arial, sans-serif; font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #f8f9fa; background: var(--background-green);
color: #333; background-image: radial-gradient(circle at center, var(--primary-color) 0%, var(--background-green) 100%);
color: var(--text-color);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
min-height: 100vh; min-height: 100vh;
text-align: center; }
/* Header und Navigation */
.navbar {
background: var(--header-bg);
border: 2px solid var(--header-border);
width: calc(100% - 4rem);
max-width: 1400px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 1.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
position: fixed;
top: 1rem;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
border-radius: 1rem;
height: 80px;
} }
.logo { .logo {
height: 40px; /* Anpassen an die Navbar-Höhe */ height: 70px;
width: auto; width: auto;
margin-right: 15px; margin-right: 15px;
margin-left: 10px;
}
/* Navigationsleiste */
.navbar {
background-color: #007bff;
width: 100%;
display: flex;
justify-content: center; /* Zentriert die Navigation */
padding: 10px 0;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
left: 0;
z-index: 1000;
} }
.navbar a { .navbar a {
display: inline-block; color: var(--inner-text-color);
color: white; text-decoration: none;
text-align: center; font-weight: 600;
padding: 14px 20px; padding: 0.5rem 1rem;
text-decoration: none; border-radius: 0.75rem;
font-weight: bold; background: rgba(255, 255, 255, 0.05);
transition: background 0.3s, color 0.3s; border: 1px solid transparent;
cursor: pointer !important; /* Wichtig: cursor-Definition für Nav-Links */ transition: all 0.3s;
} }
.navbar a:hover { .navbar a:hover {
background-color: #0056b3; background-color: rgba(255, 255, 255, 0.1);
color: #fff; transform: translateY(-1px);
cursor: pointer !important; }
/* Logo und Text im Header */
.logo-text {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.logo-text h4 {
font-size: 1rem;
color: var(--header-text);
margin: 0 !important;
}
.logo-text h1 {
font-size: 1.75rem;
font-weight: 600;
color: var(--header-border);
margin: 5px !important;
text-shadow: 1px 1px 0 var(--accent-color);
}
/* Status Container in der Navbar */
.status-container {
background: var(--inner-box-bg) !important;
border-radius: 1rem;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
gap: 1rem;
}
.status-item {
background: rgba(0, 0, 0, 0.1) !important;
color: var(--inner-text-color) !important;
padding: 0.25rem 0.75rem;
border-radius: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
margin-right: 4px;
}
.online {
background-color: #63bb67;
}
.offline {
background-color: #dc2626;
}
.ram-status {
color: var(--inner-text-color);
font-size: 0.9em;
padding: 0.25rem 0.75rem;
background: rgba(99, 187, 103, 0.1);
border-radius: 0.5rem;
} }
/* Inhalt */ /* Inhalt */
@@ -68,11 +159,12 @@ h1 {
} }
h3 { h3 {
color: #007bff; color: #007bff !important;
font-size: 24px; font-size: 24px;
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
text-align: center;
} }
/* Formulare */ /* Formulare */
@@ -145,11 +237,11 @@ button:hover {
} }
.feature { .feature {
flex: 1; flex: 1;
padding: 20px; padding: 0 20px;
background-color: #f9f9f9;
border-radius: 8px; border-radius: 8px;
margin: 0 10px; margin: 0 10px 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
border: var(--glass-border);
} }
.feature h3 { .feature h3 {
font-size: 1.4rem; font-size: 1.4rem;
@@ -158,12 +250,12 @@ button:hover {
} }
.feature p { .feature p {
font-size: 1rem; font-size: 1rem;
color: #555; color: #ffffff;
} }
p { p {
font-size: 1rem; font-size: 1rem;
color: #555; color: #ffffff;
} }
a { a {
@@ -233,6 +325,13 @@ a:hover {
margin-top: 10px; margin-top: 10px;
} }
.nfc-card-data {
padding-left: 20px !important;
color: white !important;
font-weight: bold !important;
margin: 0 auto !important;
}
.nfc-success { .nfc-success {
color: green; color: green;
} }
@@ -247,7 +346,7 @@ a:hover {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: 20px; gap: 20px;
margin-top: 20px; margin-top: 5px;
width: 100%; width: 100%;
} }
@@ -257,17 +356,21 @@ a:hover {
} }
.feature-box { .feature-box {
background: white; background: var(--inner-box-bg);
padding: 5px 20px 20px 20px; border: 1px solid rgba(99, 187, 103, 0.2);
border-radius: 8px; border-radius: 0.5rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); padding: 1rem;
margin-bottom: 20px; color: var(--inner-text-color);
margin: 10px 0 0 0;
} }
.feature-box h2 { .feature-box h2 {
color: #007bff; color: var(--inner-text-color);
font-size: 1.4rem; font-size: 1.4rem;
margin-bottom: 15px; margin-bottom: 1rem;
margin-top: 0;
color: var(--accent-color);
text-align: center;
} }
.feature-box ul { .feature-box ul {
@@ -285,27 +388,24 @@ a:hover {
width: 95%; width: 95%;
max-width: 1400px; max-width: 1400px;
margin: 0 auto; margin: 0 auto;
padding-top: 60px; padding-top: 100px;
padding-bottom: 20px;; padding-bottom: 20px;;
} }
.tray { .tray {
background: #ffffff; border-radius: 8px;
padding: 15px; padding: 15px;
margin: 10px 0; margin: 10px 0;
color: var(--inner-text-color);
background: var(--background-green);
border: 1px solid var(--border-color);
border-radius: 8px; border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
border-left: 4px solid #ffffff;
} }
.tray p { .tray p {
margin: 5px 0; margin: 5px 0;
} }
.tray b {
color: #007bff;
}
/* Responsive Design */ /* Responsive Design */
@media (max-width: 1024px) { @media (max-width: 1024px) {
.three-column-layout { .three-column-layout {
@@ -345,7 +445,7 @@ a:hover {
.nfc-data { .nfc-data {
padding: 10px; padding: 10px;
background-color: #f8f9fa; background-color: var(--primary-color);
border-radius: 4px; border-radius: 4px;
margin-top: 5px; margin-top: 5px;
width: 100%; width: 100%;
@@ -387,6 +487,7 @@ a:hover {
margin: 0; margin: 0;
grid-column: 2; grid-column: 2;
text-align: center; text-align: center;
color: var(--accent-color);
} }
.nfc-header .status-circle { .nfc-header .status-circle {
@@ -567,6 +668,7 @@ a:hover {
width: 100%; width: 100%;
font-family: inherit; font-family: inherit;
cursor: default; /* Container selbst soll normalen Cursor haben */ cursor: default; /* Container selbst soll normalen Cursor haben */
color: black;
} }
.dropdown-button { .dropdown-button {
@@ -677,23 +779,20 @@ a:hover {
/* Neue Styles für die Statistiken */ /* Neue Styles für die Statistiken */
.statistics-grid { .statistics-grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px; gap: 1rem;
margin-top: 15px; margin-top: 15px;
} }
.statistics-column { .statistics-column {
background: #f8f9fa; background: var(--inner-box-bg);
padding: 0; border: 1px solid rgba(99, 187, 103, 0.2);
border-radius: 8px; border-radius: 0.5rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
} }
.statistics-column h3 { .statistics-column h3 {
color: #007bff; color: var(--inner-text-color);
margin-bottom: 5px; border-bottom: 1px solid rgba(99, 187, 103, 0.2);
padding-bottom: 8px;
border-bottom: 2px solid #e9ecef;
font-size: 1.1rem; font-size: 1.1rem;
} }
@@ -706,8 +805,8 @@ a:hover {
.statistics-list li { .statistics-list li {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 8px 5px 0 5px; padding: 0.5rem;
border-bottom: 1px solid #e9ecef; border-bottom: 1px solid rgba(99, 187, 103, 0.1);
} }
.statistics-list li:last-child { .statistics-list li:last-child {
@@ -715,13 +814,12 @@ a:hover {
} }
.stat-label { .stat-label {
color: #495057; color: var(--inner-text-color);
font-weight: 500;
} }
.stat-value { .stat-value {
font-weight: bold; color: var(--stat-value-color);
color: #007bff; font-weight: 500;
} }
/* Responsive Design Anpassung */ /* Responsive Design Anpassung */
@@ -772,17 +870,18 @@ a:hover {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
background: var(--inner-box-bg);
border: 1px solid rgba(99, 187, 103, 0.2);
border-radius: 0.5rem;
} }
.spool-stat .stat-label { .spool-stat .stat-label {
color: #495057; color: var(--inner-text-color);
font-weight: 500;
white-space: nowrap;
} }
.spool-stat .stat-value { .spool-stat .stat-value {
font-weight: bold; color: var(--stat-value-color);
color: #007bff; font-weight: 500;
} }
/* Buttons und klickbare Elemente */ /* Buttons und klickbare Elemente */
@@ -845,20 +944,6 @@ input[type="submit"]:disabled,
animation: dots 1s steps(3, end) infinite; animation: dots 1s steps(3, end) infinite;
} }
.reboot-button {
background-color: #ff0000;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
margin-left: 10px;
cursor: pointer;
}
.reboot-button:hover {
background-color: #cc0000;
}
/* Bambu Settings Erweiterung */ /* Bambu Settings Erweiterung */
.bambu-settings { .bambu-settings {
background: white; background: white;
@@ -880,7 +965,7 @@ input[type="submit"]:disabled,
} }
.bambu-settings .input-group input { .bambu-settings .input-group input {
width: 100%; width: 95%;
} }
#bambuStatusMessage { #bambuStatusMessage {
@@ -891,11 +976,21 @@ input[type="submit"]:disabled,
justify-content: center; justify-content: center;
} }
.amsData {
border-color: black !important;
border-width: 1px !important;
}
.tray { .tray {
position: relative; position: relative;
} }
.tray-head {
color: var(--stat-value-color) !important;
text-align: center !important;
font-weight: bold !important;
}
.spool-button:hover { .spool-button:hover {
opacity: 0.8; opacity: 0.8;
} }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 571 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 535 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 835 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 814 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 648 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 488 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

+6
View File
@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
app0, app, ota_0, 0x10000, 0x180000,
app1, app, ota_1, 0x190000,0x180000,
spiffs, data, spiffs, 0x310000,0x0F0000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x9000 0x4000
3 otadata data ota 0xd000 0x2000
4 app0 app ota_0 0x10000 0x180000
5 app1 app ota_1 0x190000 0x180000
6 spiffs data spiffs 0x310000 0x0F0000
+57
View File
@@ -0,0 +1,57 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
tzapu/WiFiManager @ ^2.0.17
me-no-dev/ESP Async WebServer @ ^1.2.4
me-no-dev/AsyncTCP @ ^1.1.1
bogde/HX711 @ ^0.7.5
adafruit/Adafruit SSD1306 @ ^2.5.13
adafruit/Adafruit GFX Library @ ^1.11.11
adafruit/Adafruit PN532 @ ^1.3.3
bblanchon/ArduinoJson @ ^7.3.0
knolleary/PubSubClient @ ^2.8
digitaldragon/SSLClient @ ^1.3.2
; Enable SPIFFS upload
board_build.partitions = partitions.csv
board_build.filesystem = spiffs
board_upload.flash_size = 4MB
board_build.spiffs_create = yes
board_build.spiffs.partition = 2M
board_build.spiffs.upload_size = 2M
build_flags =
-Os
-ffunction-sections
-fdata-sections
-DNDEBUG
-mtext-section-literals
extra_scripts =
pre:gzip_files.py
pre:extra_script.py
post:$PROJECT_DIR/create_combined_binary.py
# Add custom target for combining binaries
custom_targets =
combine_binaries
# Define the build sequence
targets =
buildfs # Build SPIFFS image
buildprog # Build firmware
#combine_binaries # Combine both binaries
+51 -27
View File
@@ -30,17 +30,26 @@ int ams_count = 0;
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
AMSData ams_data[MAX_AMS]; // Definition des Arrays AMSData ams_data[MAX_AMS]; // Definition des Arrays
bool saveBambuCredentials(const String& bambu_ip, const String& bambu_serialnr, const String& bambu_accesscode) { bool saveBambuCredentials(const String& ip, const String& serialnr, const String& accesscode) {
if (BambuMqttTask) {
vTaskDelete(BambuMqttTask);
}
JsonDocument doc; JsonDocument doc;
doc["bambu_ip"] = bambu_ip; doc["bambu_ip"] = ip;
doc["bambu_accesscode"] = bambu_accesscode; doc["bambu_accesscode"] = accesscode;
doc["bambu_serialnr"] = bambu_serialnr; doc["bambu_serialnr"] = serialnr;
if (!saveJsonValue("/bambu_credentials.json", doc)) { if (!saveJsonValue("/bambu_credentials.json", doc)) {
Serial.println("Fehler beim Speichern der Bambu-Credentials."); Serial.println("Fehler beim Speichern der Bambu-Credentials.");
return false; return false;
} }
// Dynamische Speicherallokation für die globalen Pointer
bambu_ip = ip.c_str();
bambu_accesscode = accesscode.c_str();
bambu_serialnr = serialnr.c_str();
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
if (!setupMqtt()) return false; if (!setupMqtt()) return false;
@@ -183,14 +192,14 @@ bool setBambuSpool(String payload) {
int maxTemp = doc["nozzle_temp_max"]; int maxTemp = doc["nozzle_temp_max"];
String type = doc["type"].as<String>(); String type = doc["type"].as<String>();
String brand = doc["brand"].as<String>(); String brand = doc["brand"].as<String>();
String tray_info_idx = findFilamentIdx(brand, type); String tray_info_idx = (brand != "" && type != "") ? findFilamentIdx(brand, type) : "";
doc.clear(); doc.clear();
doc["print"]["sequence_id"] = 0; doc["print"]["sequence_id"] = 0;
doc["print"]["command"] = "ams_filament_setting"; doc["print"]["command"] = "ams_filament_setting";
doc["print"]["ams_id"] = amsId < 200 ? amsId-1 : 255; doc["print"]["ams_id"] = amsId < 200 ? amsId : 255;
doc["print"]["tray_id"] = trayId < 200 ? trayId-1 : 254; doc["print"]["tray_id"] = trayId < 200 ? trayId : 254;
doc["print"]["tray_color"] = color.length() == 8 ? color : color+"FF"; doc["print"]["tray_color"] = color.length() == 8 ? color : color+"FF";
doc["print"]["nozzle_temp_min"] = minTemp; doc["print"]["nozzle_temp_min"] = minTemp;
doc["print"]["nozzle_temp_max"] = maxTemp; doc["print"]["nozzle_temp_max"] = maxTemp;
@@ -293,6 +302,11 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
if (!foundExternal) hasChanges = true; if (!foundExternal) hasChanges = true;
} }
// Wenn Bambu connection changed
if (bambu_connected != doc["print"]["bambu_connected"].as<bool>()) {
hasChanges = true;
}
if (!hasChanges) return; if (!hasChanges) return;
// Fortfahren mit der bestehenden Verarbeitung, da Änderungen gefunden wurden // Fortfahren mit der bestehenden Verarbeitung, da Änderungen gefunden wurden
@@ -321,7 +335,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
//Serial.println(); //Serial.println();
// Sende die aktualisierten AMS-Daten an alle WebSocket-Clients // Sende die aktualisierten AMS-Daten an alle WebSocket-Clients
sendAmsData(nullptr); //sendAmsData(nullptr);
// Verarbeite erst die normalen AMS-Daten // Verarbeite erst die normalen AMS-Daten
for (int i = 0; i < amsArray.size() && i < 16; i++) { for (int i = 0; i < amsArray.size() && i < 16; i++) {
@@ -357,7 +371,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
} }
// Sende die aktualisierten AMS-Daten // Sende die aktualisierten AMS-Daten
sendAmsData(nullptr); //sendAmsData(nullptr);
// Erstelle JSON für WebSocket-Clients // Erstelle JSON für WebSocket-Clients
JsonDocument wsDoc; JsonDocument wsDoc;
@@ -392,6 +406,8 @@ void reconnect() {
// Loop until we're reconnected // Loop until we're reconnected
while (!client.connected()) { while (!client.connected()) {
Serial.print("Attempting MQTT connection..."); Serial.print("Attempting MQTT connection...");
bambu_connected = false;
oledShowTopRow();
// Attempt to connect // Attempt to connect
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) { if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) {
@@ -413,9 +429,6 @@ void reconnect() {
} }
void mqtt_loop(void * parameter) { void mqtt_loop(void * parameter) {
oledShowMessage("Bambu Connected");
bambu_connected = true;
oledShowTopRow();
for(;;) { for(;;) {
if (pauseBambuMqttTask) { if (pauseBambuMqttTask) {
vTaskDelay(10000); vTaskDelay(10000);
@@ -443,13 +456,16 @@ bool setupMqtt() {
return false; return false;
} }
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "") { if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
{
sslClient.setCACert(root_ca); sslClient.setCACert(root_ca);
sslClient.setInsecure(); sslClient.setInsecure();
client.setServer(bambu_ip, 8883); client.setServer(bambu_ip, 8883);
// Verbinden mit dem MQTT-Server // Verbinden mit dem MQTT-Server
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode)) { bool connected = true;
if (client.connect(bambu_serialnr, bambu_username, bambu_accesscode))
{
client.setCallback(mqtt_callback); client.setCallback(mqtt_callback);
client.setBufferSize(5120); client.setBufferSize(5120);
// Optional: Topic abonnieren // Optional: Topic abonnieren
@@ -458,24 +474,32 @@ bool setupMqtt() {
Serial.println("MQTT-Client initialisiert"); Serial.println("MQTT-Client initialisiert");
oledShowTopRow(); oledShowTopRow();
oledShowMessage("Bambu Connected");
xTaskCreatePinnedToCore( bambu_connected = true;
mqtt_loop, /* Function to implement the task */ oledShowTopRow();
"BambuMqtt", /* Name of the task */ }
10000, /* Stack size in words */ else
NULL, /* Task input parameter */ {
mqttTaskPrio, /* Priority of the task */
&BambuMqttTask, /* Task handle. */
mqttTaskCore); /* Core where the task should run */
} else {
Serial.println("Fehler: Konnte sich nicht beim MQTT-Server anmelden"); Serial.println("Fehler: Konnte sich nicht beim MQTT-Server anmelden");
oledShowMessage("Bambu Connection Failed"); oledShowMessage("Bambu Connection Failed");
oledShowTopRow(); oledShowTopRow();
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
return false; connected = false;
} }
} else {
xTaskCreatePinnedToCore(
mqtt_loop, /* Function to implement the task */
"BambuMqtt", /* Name of the task */
10000, /* Stack size in words */
NULL, /* Task input parameter */
mqttTaskPrio, /* Priority of the task */
&BambuMqttTask, /* Task handle. */
mqttTaskCore); /* Core where the task should run */
if (!connected) return false;
}
else
{
Serial.println("Fehler: Keine MQTT-Daten vorhanden"); Serial.println("Fehler: Keine MQTT-Daten vorhanden");
oledShowMessage("Bambu Credentials Missing"); oledShowMessage("Bambu Credentials Missing");
oledShowTopRow(); oledShowTopRow();
+2 -1
View File
@@ -7,12 +7,13 @@ bool saveJsonValue(const char* filename, const JsonDocument& doc) {
Serial.println(filename); Serial.println(filename);
return false; return false;
} }
return true;
if (serializeJson(doc, file) == 0) { if (serializeJson(doc, file) == 0) {
Serial.println("Fehler beim Serialisieren von JSON."); Serial.println("Fehler beim Serialisieren von JSON.");
file.close(); file.close();
return false; return false;
} }
file.close(); file.close();
return true; return true;
} }
+10
View File
@@ -74,6 +74,10 @@ void setup() {
unsigned long lastWeightReadTime = 0; unsigned long lastWeightReadTime = 0;
const unsigned long weightReadInterval = 1000; // 1 second const unsigned long weightReadInterval = 1000; // 1 second
unsigned long lastAmsSendTime = 0;
const unsigned long amsSendInterval = 60000; // 1 minute
uint8_t weightSend = 0; uint8_t weightSend = 0;
int16_t lastWeight = 0; int16_t lastWeight = 0;
uint8_t wifiErrorCounter = 0; uint8_t wifiErrorCounter = 0;
@@ -95,6 +99,12 @@ void loop() {
// Falls WifiManager im nicht blockenden Modus ist // Falls WifiManager im nicht blockenden Modus ist
//if(wm_nonblocking) wm.process(); //if(wm_nonblocking) wm.process();
// Send AMS Data min every Minute
if (currentMillis - lastAmsSendTime >= amsSendInterval) {
lastAmsSendTime = currentMillis;
sendAmsData(nullptr);
}
// Ausgabe der Waage auf Display // Ausgabe der Waage auf Display
if (pauseMainTask == 0 && weight != lastWeight && hasReadRfidTag == 0) if (pauseMainTask == 0 && weight != lastWeight && hasReadRfidTag == 0)
{ {
+2
View File
@@ -379,6 +379,8 @@ void scanRfidTask(void * parameter) {
// Wenn geschrieben wird Schleife aussetzen // Wenn geschrieben wird Schleife aussetzen
if (hasReadRfidTag != 3) if (hasReadRfidTag != 3)
{ {
yield();
uint8_t success; uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; uint8_t uidLength;
+17 -5
View File
@@ -24,8 +24,8 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
// Sende die AMS-Daten an den neuen Client // Sende die AMS-Daten an den neuen Client
sendAmsData(client); sendAmsData(client);
sendNfcData(client); sendNfcData(client);
foundNfcTag(client, hasReadRfidTag); foundNfcTag(client, 0);
sendWriteResult(client, 0); sendWriteResult(client, 3);
} else if (type == WS_EVT_DISCONNECT) { } else if (type == WS_EVT_DISCONNECT) {
Serial.println("Client getrennt."); Serial.println("Client getrennt.");
} else if (type == WS_EVT_DATA) { } else if (type == WS_EVT_DATA) {
@@ -213,9 +213,16 @@ void setupWebserver(AsyncWebServer &server) {
JsonDocument doc; JsonDocument doc;
if (loadJsonValue("/bambu_credentials.json", doc) && doc.containsKey("bambu_ip")) { if (loadJsonValue("/bambu_credentials.json", doc) && doc.containsKey("bambu_ip")) {
html.replace("{{bambuIp}}", doc["bambu_ip"].as<String>() ? doc["bambu_ip"].as<String>() : ""); String bambuIp = doc["bambu_ip"].as<String>();
html.replace("{{bambuSerial}}", doc["bambu_serialnr"].as<String>() ? doc["bambu_serialnr"].as<String>() : ""); String bambuSerial = doc["bambu_serialnr"].as<String>();
html.replace("{{bambuCode}}", doc["bambu_accesscode"].as<String>() ? doc["bambu_accesscode"].as<String>() : ""); String bambuCode = doc["bambu_accesscode"].as<String>();
bambuIp.trim();
bambuSerial.trim();
bambuCode.trim();
html.replace("{{bambuIp}}", bambuIp ? bambuIp : "");
html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : "");
html.replace("{{bambuCode}}", bambuCode ? bambuCode : "");
} }
request->send(200, "text/html", html); request->send(200, "text/html", html);
@@ -251,6 +258,11 @@ void setupWebserver(AsyncWebServer &server) {
bambu_serialnr.trim(); bambu_serialnr.trim();
bambu_accesscode.trim(); bambu_accesscode.trim();
if (bambu_ip.length() == 0 || bambu_serialnr.length() == 0 || bambu_accesscode.length() == 0) {
request->send(400, "application/json", "{\"success\": false, \"error\": \"Empty parameter\"}");
return;
}
bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode); bool success = saveBambuCredentials(bambu_ip, bambu_serialnr, bambu_accesscode);
request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}"); request->send(200, "application/json", "{\"healthy\": " + String(success ? "true" : "false") + "}");