Compare commits

...

32 Commits

Author SHA1 Message Date
7cbd34bc91 docs: update changelog and header for version v1.5.8
All checks were successful
Release Workflow / detect-provider (push) Successful in 52s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 3m19s
2025-08-10 14:39:44 +02:00
f7484f635e docs: update platformio.ini for version v1.5.8 2025-08-10 14:39:44 +02:00
90ce30215f Merge pull request #45 from janecker/nfc_write_improvements
Nfc write improvements
2025-08-10 14:38:11 +02:00
Jan Philipp Ecker
5fa93f2695 Adds a link to the spool in spoolman when reading a spool tag
Adds a link to the website that lets the user directly jump to the spool in spoolman that is currently scanned.
2025-08-08 18:14:26 +02:00
Jan Philipp Ecker
4706152022 Introduces periodic Spoolman Healthcheck
Introduces a spoolman healthcheck that is executed every 60 seconds. Also fixes a bug with the periodic wifi update.
2025-08-08 18:00:25 +02:00
Jan Philipp Ecker
5509d98969 Fixes issue that scale not calibrated message was not shown
There was no warning any more if the scale is not calibrated. This change fixes that.
2025-08-08 16:16:39 +02:00
Jan Philipp Ecker
a7c99d3f26 Improves init - NFC reading now only starts after boot is finished
NFC tags that are on the scale during startup will only be read after the boot sequence is finished.
2025-08-08 15:39:10 +02:00
Jan Philipp Ecker
89a5728cc0 Improves NFC writing workaround and removes debug output
Improved version of the NFC writing workaround. The task is no longer suspended. There is now a suspend request and a suspend state variable that is used to communicate between the writing and the reading task. The reading is stopped gracefully to prevent resets during writing.
2025-08-08 15:33:08 +02:00
Jan Philipp Ecker
b95497aec2 Further improvements on NFC writing
Fixes some issues related to tag writing. Allos writing of tags that are already on the scale when pressing the write button, but introduces a confirmation dialog before doing so. Also first test to fix reset issue when trying to write tags.
2025-08-07 21:12:01 +02:00
a156cac18e Merge pull request #44 from janecker/graphics_rework
Graphics rework
2025-08-06 08:27:24 +02:00
Jan Philipp Ecker
09f4c43f89 Fixes types and some issues in the new graphics
Fixes further issues with the new graphics. Fixes some typos. Adds progress bar for upgrade process.
2025-08-05 19:43:05 +02:00
Jan Philipp Ecker
ec0e544f30 Fixes typos in upgrade page
Fixes names of binary files on the upgrade page.
2025-08-05 19:41:06 +02:00
Jan Philipp Ecker
b6d82c8afe Graphic rework of the NFC writing process
Introduces the new graphics for the NFC writing process. Also fixes some minor display bugs. Hides the service status icons during boot time. Fixes bugs in NFC write process where mutliple parallel API calls a created. Fixes a bug where spoolman is updated if a location tag is written (which is not required or correct).
2025-08-05 17:44:59 +02:00
Jan Philipp Ecker
97a1368747 Reworks graphics of tag reading and some api fixes
Reworks the graphics of the NFC-Tag reading process of spool and location tags. Introduces progress bar for reading process. Also first re-work of the spoolman availability (not fixed completly yet). Also fixes an issue where the API request to spoolman and octoprint was sent in parallel. This now happens sequentially to reduce heap load.
2025-08-03 16:51:09 +02:00
Jan Philipp Ecker
85a9bcf8bd Remove unused parameter of sendNfcData()
Removes unused client parameter of the sendNfcData function.
2025-08-02 23:05:56 +02:00
Jan Philipp Ecker
c450df59aa Replaces usage of String with const char* in heap debug function
Replaces String with const char* in printHeapDebugData to reduce heap fragmentation.
2025-08-02 22:53:38 +02:00
Jan Philipp Ecker
722ef421cb Reworks startup graphics and timings
Reworks the graphics during the startup. Introduces a progress bar to visualize how much of the boot is completed. Also changes the the optics of the bambu, spoolman and wifi icons. They are now always displayed but they will be striked out if not working and they will start blinking. Also removes some unnessesary waits.
2025-08-02 22:52:19 +02:00
b0cd731c5a docs: update changelog and header for version v1.5.7
All checks were successful
Release Workflow / detect-provider (push) Successful in 3s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Successful in 3m4s
2025-07-28 09:51:55 +02:00
f022bee578 docs: update platformio.ini for version v1.5.7 2025-07-28 09:51:55 +02:00
3286b64836 refactor: clean up unused variables and improve .gitignore entries 2025-07-28 09:51:46 +02:00
739fe7e764 docs: update changelog and header for version v1.5.6
Some checks failed
Release Workflow / detect-provider (push) Successful in 4s
Release Workflow / github-release (push) Has been skipped
Release Workflow / gitea-release (push) Failing after 2m22s
2025-07-28 09:32:35 +02:00
5f8953a19d docs: update webpages for version v1.5.6 2025-07-28 09:32:35 +02:00
c919eeb848 docs: update platformio.ini for version v1.5.6 2025-07-28 09:32:30 +02:00
43177c670e Merge pull request #42 from janecker/configuration_nvs_storage
Changes configuration storage of spoolman and bambu values
2025-07-28 09:31:33 +02:00
1b50694f5f Merge branch 'main' into configuration_nvs_storage 2025-07-28 09:31:23 +02:00
48edde8557 Merge pull request #41 from janecker/memory_leak_fixes
Memory leak fixes
2025-07-28 09:26:30 +02:00
Jan Philipp Ecker
cb5d8ac10a Changes configuration storage of spoolman and bambu values
Change that moves configuration values of spoolman and bambu credentials to use NVS storage. Also fixes some typos and missing translation.
2025-07-27 17:33:09 +02:00
Jan Philipp Ecker
bf48c6d4e1 Fixes compiler warnings in nfc
Replaces the depricated function call containsKey() with is<T>() of JsonDocument.
2025-07-26 22:52:10 +02:00
Jan Philipp Ecker
5d2d5e9ee1 Adds ENABLE_HEAP_DEBUGGING define as comment to the build flags
Adds the new ENABLE_HEAP_DEBUGGING define to the build falgs. The option is commented out, but it makes it easier to quickly enable it
2025-07-26 22:50:08 +02:00
Jan Philipp Ecker
7e76612bb4 Adds data directory and further .vscode files to to .gitignore
Adds the whole data folder that is created during build of the project and further visual studio code related files to the .gitignore file.
2025-07-26 22:39:37 +02:00
Jan Philipp Ecker
f038020042 Memory leak fixes in api and nfc, location tag fix
Fixes multiple potential memory leaks in API and NFC. Also fixes an issue in the new locaiton tag feature that could lead to multiple parallel API requests. This could cause memory leak issues but also result in wrong weights being registered for a spool.
2025-07-26 22:36:04 +02:00
Jan Philipp Ecker
8343fe887b Introduces new heap debugging feature and fixes some memory leaks in website feature
Introduces a new define HEAP_DEBUG_MESSAGE(location) that can be used to instrument the code to get heap information output on the Serial output. It can be enabled via the define ENABLE_HEAP_DEBUGGING. Also fixes some memory leaks in the website part of the project.
2025-07-26 22:14:58 +02:00
28 changed files with 971 additions and 638 deletions

41
.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
.pio
.vscode/
.aider*
.DS_Store
._*
**/.DS_Store
**/.Spotlight-V100
**/.Trashes
**/.fseventsd
.AppleDouble
**/.DS_Store
**/.Spotlight-V100
**/.Trashes
**/.fseventsd
.AppleDouble
.aider.chat.history.md
.aider.input.history
.DS_Store
.gitignore
.aider.tags.cache.v3/cache.db
.aider.tags.cache.v3/cache.db-shm
.aider.tags.cache.v3/cache.db-wal
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode/extensions.json
.vscode/launch.json
include/README
lib/README
test/README
.aider*
data/*
!data/
!data/.gitkeep
html/bambu_credentials.json
html/spoolman_url.json
_local/*
website/*
release.sh
.github/copilot-instructions.md
data

View File

@@ -1,5 +1,54 @@
# Changelog # Changelog
## [1.5.8] - 2025-08-10
### Added
- Adds a link to the spool in spoolman when reading a spool tag
- Fixes types and some issues in the new graphics
### Changed
- update platformio.ini for version v1.5.8
- Merge pull request #45 from janecker/nfc_write_improvements
- Introduces periodic Spoolman Healthcheck
- Improves init - NFC reading now only starts after boot is finished
- Further improvements on NFC writing
- Merge pull request #44 from janecker/graphics_rework
- Graphic rework of the NFC writing process
- Remove unused parameter of sendNfcData()
- Reworks startup graphics and timings
### Fixed
- Fixes issue that scale not calibrated message was not shown
- Improves NFC writing workaround and removes debug output
- Fixes typos in upgrade page
- Reworks graphics of tag reading and some api fixes
- Replaces usage of String with const char* in heap debug function
## [1.5.7] - 2025-07-28
### Changed
- update platformio.ini for version v1.5.7
- clean up unused variables and improve .gitignore entries
## [1.5.6] - 2025-07-28
### Added
- Adds ENABLE_HEAP_DEBUGGING define as comment to the build flags
- Adds data directory and further .vscode files to to .gitignore
- Introduces new heap debugging feature and fixes some memory leaks in website feature
### Changed
- update webpages for version v1.5.6
- update platformio.ini for version v1.5.6
- Merge pull request #42 from janecker/configuration_nvs_storage
- Merge branch 'main' into configuration_nvs_storage
- Changes configuration storage of spoolman and bambu values
### Fixed
- Merge pull request #41 from janecker/memory_leak_fixes
- Fixes compiler warnings in nfc
- Memory leak fixes in api and nfc, location tag fix
## [1.5.5] - 2025-07-22 ## [1.5.5] - 2025-07-22
### Added ### Added
- Fixes some issues with the new location tags - Fixes some issues with the new location tags

BIN
html/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"bambu_ip": "192.168.1.14", "bambu_accesscode": "22772584", "bambu_serialnr": "01P00C492600230","autoSendToBambu":true,"autoSendTime": 60}

View File

@@ -7,6 +7,7 @@ let heartbeatTimer = null;
let lastHeartbeatResponse = Date.now(); let lastHeartbeatResponse = Date.now();
const HEARTBEAT_TIMEOUT = 20000; const HEARTBEAT_TIMEOUT = 20000;
let reconnectTimer = null; let reconnectTimer = null;
let spoolDetected = false;
// WebSocket Funktionen // WebSocket Funktionen
function startHeartbeat() { function startHeartbeat() {
@@ -508,12 +509,15 @@ function updateNfcStatusIndicator(data) {
if (data.found === 0) { if (data.found === 0) {
// Kein NFC Tag gefunden // Kein NFC Tag gefunden
indicator.className = 'status-circle'; indicator.className = 'status-circle';
spoolDetected = false;
} else if (data.found === 1) { } else if (data.found === 1) {
// NFC Tag erfolgreich gelesen // NFC Tag erfolgreich gelesen
indicator.className = 'status-circle success'; indicator.className = 'status-circle success';
spoolDetected = true;
} else { } else {
// Fehler beim Lesen // Fehler beim Lesen
indicator.className = 'status-circle error'; indicator.className = 'status-circle error';
spoolDetected = true;
} }
} }
@@ -574,7 +578,7 @@ function updateNfcData(data) {
`; `;
// Spoolman ID anzeigen // Spoolman ID anzeigen
html += `<p><strong>Spoolman ID:</strong> ${data.sm_id || 'No Spoolman ID'}</p>`; html += `<p><strong>Spoolman ID:</strong> ${data.sm_id} (<a href="${spoolmanUrl}/spool/show/${data.sm_id}">Open in Spoolman</a>)</p>`;
} }
else if(data.location) else if(data.location)
{ {
@@ -618,76 +622,83 @@ function updateNfcData(data) {
} }
function writeNfcTag() { function writeNfcTag() {
const selectedText = document.getElementById("selected-filament").textContent; if(!spoolDetected || confirm("Are you sure you want to overwrite the Tag?") == true){
if (selectedText === "Please choose...") { const selectedText = document.getElementById("selected-filament").textContent;
alert('Please select a Spool first.'); if (selectedText === "Please choose...") {
return; alert('Please select a Spool first.');
} return;
}
const spoolsData = window.getSpoolData(); const spoolsData = window.getSpoolData();
const selectedSpool = spoolsData.find(spool => const selectedSpool = spoolsData.find(spool =>
`${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText `${spool.id} | ${spool.filament.name} (${spool.filament.material})` === selectedText
); );
if (!selectedSpool) { if (!selectedSpool) {
alert('Ausgewählte Spule konnte nicht gefunden werden.'); alert('Ausgewählte Spule konnte nicht gefunden werden.');
return; return;
} }
// Temperaturwerte korrekt extrahieren // Temperaturwerte korrekt 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 = String(selectedSpool.filament.nozzle_temperature[0]); minTemp = String(selectedSpool.filament.nozzle_temperature[0]);
maxTemp = String(selectedSpool.filament.nozzle_temperature[1]); maxTemp = String(selectedSpool.filament.nozzle_temperature[1]);
} }
// Erstelle das NFC-Datenpaket mit korrekten Datentypen // Erstelle das NFC-Datenpaket mit korrekten Datentypen
const nfcData = { const nfcData = {
color_hex: selectedSpool.filament.color_hex || "FFFFFF", color_hex: selectedSpool.filament.color_hex || "FFFFFF",
type: selectedSpool.filament.material, type: selectedSpool.filament.material,
min_temp: minTemp, min_temp: minTemp,
max_temp: maxTemp, max_temp: maxTemp,
brand: selectedSpool.filament.vendor.name, brand: selectedSpool.filament.vendor.name,
sm_id: String(selectedSpool.id) // Konvertiere zu String sm_id: String(selectedSpool.id) // Konvertiere zu String
}; };
if (socket?.readyState === WebSocket.OPEN) { if (socket?.readyState === WebSocket.OPEN) {
const writeButton = document.getElementById("writeNfcButton"); const writeButton = document.getElementById("writeNfcButton");
writeButton.classList.add("writing"); writeButton.classList.add("writing");
writeButton.textContent = "Writing"; writeButton.textContent = "Writing";
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: 'writeNfcTag', type: 'writeNfcTag',
payload: nfcData tagType: 'spool',
})); payload: nfcData
} else { }));
alert('Not connected to Server. Please check connection.'); } else {
alert('Not connected to Server. Please check connection.');
}
} }
} }
function writeLocationNfcTag() { function writeLocationNfcTag() {
const selectedText = document.getElementById("locationSelect").value; if(!spoolDetected || confirm("Are you sure you want to overwrite the Tag?") == true){
if (selectedText === "Please choose...") { const selectedText = document.getElementById("locationSelect").value;
alert('Please select a location first.'); if (selectedText === "Please choose...") {
return; alert('Please select a location first.');
} return;
// Erstelle das NFC-Datenpaket mit korrekten Datentypen }
const nfcData = { // Erstelle das NFC-Datenpaket mit korrekten Datentypen
location: String(selectedText) const nfcData = {
}; location: String(selectedText)
};
if (socket?.readyState === WebSocket.OPEN) {
const writeButton = document.getElementById("writeLocationNfcButton"); if (socket?.readyState === WebSocket.OPEN) {
writeButton.classList.add("writing"); const writeButton = document.getElementById("writeLocationNfcButton");
writeButton.textContent = "Writing"; writeButton.classList.add("writing");
socket.send(JSON.stringify({ writeButton.textContent = "Writing";
type: 'writeNfcTag', socket.send(JSON.stringify({
payload: nfcData type: 'writeNfcTag',
})); tagType: 'location',
} else { payload: nfcData
alert('Not connected to Server. Please check connection.'); }));
} else {
alert('Not connected to Server. Please check connection.');
}
} }
} }

View File

@@ -146,20 +146,20 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Set URL/IP to your Spoolman-Instanz</h5> <h5 class="card-title">Set URL/IP to your Spoolman instance</h5>
<input type="text" id="spoolmanUrl" placeholder="http://ip-or-url-of-your-spoolman-instanz:port"> <input type="text" id="spoolmanUrl" onkeydown="if(event.keyCode == 13) document.getElementById('btnSaveSpoolmanUrl').click()" placeholder="http://ip-or-url-of-your-spoolman-instance:port">
<h5 class="card-title">If you want to enable sending Spool to Spoolman Octoprint Plugin:</h5> <h5 class="card-title">If you want to enable sending the spool to the Spoolman Octoprint plugin:</h5>
<p> <p>
<input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin <input type="checkbox" id="spoolmanOctoEnabled" {{spoolmanOctoEnabled}} onchange="toggleOctoFields()"> Send to Octo-Plugin
</p> </p>
<div id="octoFields" style="display: none;"> <div id="octoFields" style="display: none;">
<p> <p>
<input type="text" id="spoolmanOctoUrl" placeholder="http://ip-or-url-of-your-octoprint-instanz:port" value="{{spoolmanOctoUrl}}"> <input type="text" id="spoolmanOctoUrl" placeholder="http://ip-or-url-of-your-octoprint-instance:port" value="{{spoolmanOctoUrl}}">
<input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}"> <input type="text" id="spoolmanOctoToken" placeholder="Your Octoprint Token" value="{{spoolmanOctoToken}}">
</p> </p>
</div> </div>
<button onclick="checkSpoolmanInstance()">Save Spoolman URL</button> <button id="btnSaveSpoolmanUrl" onclick="checkSpoolmanInstance()">Save Spoolman URL</button>
<p id="statusMessage"></p> <p id="statusMessage"></p>
</div> </div>
</div> </div>
@@ -169,16 +169,16 @@
<h5 class="card-title">Bambu Lab Printer Credentials</h5> <h5 class="card-title">Bambu Lab Printer Credentials</h5>
<div class="bambu-settings"> <div class="bambu-settings">
<div class="input-group"> <div class="input-group">
<label for="bambuIp">Bambu Drucker IP-Adresse:</label> <label for="bambuIp">Bambu Printer IP Address:</label>
<input type="text" id="bambuIp" placeholder="192.168.1.xxx" value="{{bambuIp}}"> <input type="text" id="bambuIp" placeholder="192.168.1.xxx" value="{{bambuIp}}">
</div> </div>
<div class="input-group"> <div class="input-group">
<label for="bambuSerial">Drucker Seriennummer:</label> <label for="bambuSerial">Printer Serial Number:</label>
<input type="text" id="bambuSerial" placeholder="BBLXXXXXXXX" value="{{bambuSerial}}"> <input type="text" id="bambuSerial" placeholder="BBLXXXXXXXX" value="{{bambuSerial}}">
</div> </div>
<div class="input-group"> <div class="input-group">
<label for="bambuCode">Access Code:</label> <label for="bambuCode">Access Code:</label>
<input type="text" id="bambuCode" placeholder="Access Code vom Drucker" value="{{bambuCode}}"> <input type="text" id="bambuCode" placeholder="Access Code of the printer" value="{{bambuCode}}">
</div> </div>
<hr> <hr>
<p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p> <p>If activated, FilaMan will automatically update the next filled tray with the last scanned and weighed spool.</p>

1
html/spoolman_url.json Normal file
View File

@@ -0,0 +1 @@
{"url": "http://192.168.1.5:7912", "octoEnabled": true, "octoUrl": "http://192.168.1.17:5001", "octoToken": "O5zZ58mXRAyeGpVEj2ZZj-UPAPqJ2N7JgtD36mw1M4g"}

View File

@@ -57,7 +57,7 @@
<div class="update-options"> <div class="update-options">
<div class="update-section"> <div class="update-section">
<h2>Firmware Update</h2> <h2>Firmware Update</h2>
<p>Upload a new firmware file (filaman_*.bin)</p> <p>Upload a new firmware file (upgrade_filaman_firmware_*.bin)</p>
<div class="update-form"> <div class="update-form">
<form id="firmwareForm" enctype='multipart/form-data' data-type="firmware"> <form id="firmwareForm" enctype='multipart/form-data' data-type="firmware">
<input type='file' name='update' accept='.bin' required> <input type='file' name='update' accept='.bin' required>
@@ -68,7 +68,7 @@
<div class="update-section"> <div class="update-section">
<h2>Webpage Update</h2> <h2>Webpage Update</h2>
<p>Upload a new webpage file (webpage_*.bin)</p> <p>Upload a new webpage file (upgrade_filaman_website_*.bin)</p>
<div class="update-form"> <div class="update-form">
<form id="webpageForm" enctype='multipart/form-data' data-type="webpage"> <form id="webpageForm" enctype='multipart/form-data' data-type="webpage">
<input type='file' name='update' accept='.bin' required> <input type='file' name='update' accept='.bin' required>

View File

@@ -9,7 +9,7 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[common] [common]
version = "1.5.5" version = "1.5.8"
to_old_version = "1.5.0" to_old_version = "1.5.0"
## ##
@@ -52,6 +52,7 @@ build_flags =
-mtext-section-literals -mtext-section-literals
-DVERSION=\"${common.version}\" -DVERSION=\"${common.version}\"
-DTOOLDVERSION=\"${common.to_old_version}\" -DTOOLDVERSION=\"${common.to_old_version}\"
#-DENABLE_HEAP_DEBUGGING
-DASYNCWEBSERVER_REGEX -DASYNCWEBSERVER_REGEX
#-DCORE_DEBUG_LEVEL=3 #-DCORE_DEBUG_LEVEL=3
-DCONFIG_ARDUHAL_LOG_COLORS=1 -DCONFIG_ARDUHAL_LOG_COLORS=1

View File

@@ -2,13 +2,20 @@
#include <HTTPClient.h> #include <HTTPClient.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "commonFS.h" #include "commonFS.h"
#include <Preferences.h>
#include "debug.h"
volatile spoolmanApiStateType spoolmanApiState = API_INIT; volatile spoolmanApiStateType spoolmanApiState = API_IDLE;
//bool spoolman_connected = false; //bool spoolman_connected = false;
String spoolmanUrl = ""; String spoolmanUrl = "";
bool octoEnabled = false; bool octoEnabled = false;
bool sendOctoUpdate = false;
String octoUrl = ""; String octoUrl = "";
String octoToken = ""; String octoToken = "";
uint16_t remainingWeight = 0;
bool spoolmanConnected = false;
bool spoolmanExtraFieldsChecked = false;
TaskHandle_t* apiTask;
struct SendToApiParams { struct SendToApiParams {
SpoolmanApiRequestType requestType; SpoolmanApiRequestType requestType;
@@ -87,6 +94,13 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
} }
void sendToApi(void *parameter) { void sendToApi(void *parameter) {
HEAP_DEBUG_MESSAGE("sendToApi begin");
// Wait until API is IDLE
while(spoolmanApiState != API_IDLE){
Serial.println("Waiting!");
yield();
}
spoolmanApiState = API_TRANSMITTING; spoolmanApiState = API_TRANSMITTING;
SendToApiParams* params = (SendToApiParams*)parameter; SendToApiParams* params = (SendToApiParams*)parameter;
@@ -120,23 +134,52 @@ void sendToApi(void *parameter) {
Serial.print("Fehler beim Parsen der JSON-Antwort: "); Serial.print("Fehler beim Parsen der JSON-Antwort: ");
Serial.println(error.c_str()); Serial.println(error.c_str());
} else { } else {
if (requestType == API_REQUEST_SPOOL_WEIGHT_UPDATE) { switch(requestType){
uint16_t remaining_weight = doc["remaining_weight"].as<float>(); case API_REQUEST_SPOOL_WEIGHT_UPDATE:
remainingWeight = doc["remaining_weight"].as<uint16_t>();
Serial.print("Aktuelles Gewicht: "); Serial.print("Aktuelles Gewicht: ");
Serial.println(remaining_weight); Serial.println(remainingWeight);
oledShowMessage("Remaining: " + String(remaining_weight) + "g"); //oledShowMessage("Remaining: " + String(remaining_weight) + "g");
if(!octoEnabled){
// TBD: Do not use Strings...
oledShowProgressBar(1, 1, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
remainingWeight = 0;
}else{
// ocoto is enabled, trigger octo update
sendOctoUpdate = true;
}
break;
case API_REQUEST_SPOOL_LOCATION_UPDATE:
oledShowProgressBar(1, 1, "Loc. Tag", "Done!");
break;
case API_REQUEST_SPOOL_TAG_ID_UPDATE:
oledShowProgressBar(1, 1, "Write Tag", "Done!");
break;
case API_REQUEST_OCTO_SPOOL_UPDATE:
// TBD: Do not use Strings...
oledShowProgressBar(5, 5, "Spool Tag", ("Done: " + String(remainingWeight) + " g remain").c_str());
remainingWeight = 0;
break;
} }
else if ( requestType == API_REQUEST_SPOOL_LOCATION_UPDATE) {
oledShowMessage("Location updated!");
}
vTaskDelay(3000 / portTICK_PERIOD_MS);
doc.clear();
} }
doc.clear();
} else { } else {
switch(requestType){
case API_REQUEST_SPOOL_WEIGHT_UPDATE:
case API_REQUEST_SPOOL_LOCATION_UPDATE:
case API_REQUEST_SPOOL_TAG_ID_UPDATE:
oledShowProgressBar(1, 1, "Failure!", "Spoolman update");
break;
case API_REQUEST_OCTO_SPOOL_UPDATE:
oledShowProgressBar(1, 1, "Failure!", "Octoprint update");
break;
case API_REQUEST_BAMBU_UPDATE:
oledShowProgressBar(1, 1, "Failure!", "Bambu update");
break;
}
Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode)); Serial.println("Fehler beim Senden an Spoolman! HTTP Code: " + String(httpCode));
oledShowMessage("Spoolman update failed");
// TBD: really required?
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
@@ -145,11 +188,14 @@ void sendToApi(void *parameter) {
// Speicher freigeben // Speicher freigeben
delete params; delete params;
vTaskDelete(NULL); HEAP_DEBUG_MESSAGE("sendToApi end");
spoolmanApiState = API_IDLE; spoolmanApiState = API_IDLE;
vTaskDelete(NULL);
} }
bool updateSpoolTagId(String uidString, const char* payload) { bool updateSpoolTagId(String uidString, const char* payload) {
oledShowProgressBar(2, 3, "Write Tag", "Update Spoolman");
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload); DeserializationError error = deserializeJson(doc, payload);
@@ -169,6 +215,8 @@ bool updateSpoolTagId(String uidString, const char* payload) {
Serial.print("Update Spule mit URL: "); Serial.print("Update Spule mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
doc.clear();
// Update Payload erstellen // Update Payload erstellen
JsonDocument updateDoc; JsonDocument updateDoc;
updateDoc["extra"]["nfc_id"] = "\""+uidString+"\""; updateDoc["extra"]["nfc_id"] = "\""+uidString+"\"";
@@ -195,18 +243,21 @@ bool updateSpoolTagId(String uidString, const char* payload) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) apiTask // Task-Handle (nicht benötigt)
); );
updateDoc.clear(); updateDoc.clear();
// Update Spool weight // Update Spool weight
if (weight > 10) updateSpoolWeight(doc["sm_id"].as<String>(), weight); //TBD: how to handle this with spool and locatin tags? Also potential parallel access again
//if (weight > 10) updateSpoolWeight(doc["sm_id"].as<String>(), weight);
return true; return true;
} }
uint8_t updateSpoolWeight(String spoolId, uint16_t weight) { uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
HEAP_DEBUG_MESSAGE("updateSpoolWeight begin");
oledShowProgressBar(3, octoEnabled?5:4, "Spool Tag", "Spoolman update");
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId + "/measure"; String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId + "/measure";
Serial.print("Update Spule mit URL: "); Serial.print("Update Spule mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
@@ -222,6 +273,7 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
SendToApiParams* params = new SendToApiParams(); SendToApiParams* params = new SendToApiParams();
if (params == nullptr) { if (params == nullptr) {
// TBD: reset ESP instead of showing a message
Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren."); Serial.println("Fehler: Kann Speicher für Task-Parameter nicht allokieren.");
return 0; return 0;
} }
@@ -237,15 +289,20 @@ uint8_t updateSpoolWeight(String spoolId, uint16_t weight) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) apiTask // Task-Handle (nicht benötigt)
); );
updateDoc.clear(); updateDoc.clear();
HEAP_DEBUG_MESSAGE("updateSpoolWeight end");
return 1; return 1;
} }
uint8_t updateSpoolLocation(String spoolId, String location){ uint8_t updateSpoolLocation(String spoolId, String location){
HEAP_DEBUG_MESSAGE("updateSpoolLocation begin");
oledShowProgressBar(3, octoEnabled?5:4, "Loc. Tag", "Spoolman update");
String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId; String spoolsUrl = spoolmanUrl + apiUrl + "/spool/" + spoolId;
Serial.print("Update Spule mit URL: "); Serial.print("Update Spule mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
@@ -269,22 +326,30 @@ uint8_t updateSpoolLocation(String spoolId, String location){
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
// Erstelle die Task
BaseType_t result = xTaskCreate( if(apiTask == nullptr){
sendToApi, // Task-Funktion // Erstelle die Task
"SendToApiTask", // Task-Name BaseType_t result = xTaskCreate(
6144, // Stackgröße in Bytes sendToApi, // Task-Funktion
(void*)params, // Parameter "SendToApiTask", // Task-Name
0, // Priorität 6144, // Stackgröße in Bytes
NULL // Task-Handle (nicht benötigt) (void*)params, // Parameter
); 0, // Priorität
apiTask // Task-Handle
);
}else{
Serial.println("Not spawning new task, API still active!");
}
updateDoc.clear(); updateDoc.clear();
HEAP_DEBUG_MESSAGE("updateSpoolLocation end");
return 1; return 1;
} }
bool updateSpoolOcto(int spoolId) { bool updateSpoolOcto(int spoolId) {
oledShowProgressBar(4, octoEnabled?5:4, "Spool Tag", "Octoprint update");
String spoolsUrl = octoUrl + "/plugin/Spoolman/selectSpool"; String spoolsUrl = octoUrl + "/plugin/Spoolman/selectSpool";
Serial.print("Update Spule in Octoprint mit URL: "); Serial.print("Update Spule in Octoprint mit URL: ");
Serial.println(spoolsUrl); Serial.println(spoolsUrl);
@@ -316,7 +381,7 @@ bool updateSpoolOcto(int spoolId) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) apiTask // Task-Handle (nicht benötigt)
); );
updateDoc.clear(); updateDoc.clear();
@@ -345,6 +410,10 @@ bool updateSpoolBambuData(String payload) {
String updatePayload; String updatePayload;
serializeJson(updateDoc, updatePayload); serializeJson(updateDoc, updatePayload);
doc.clear();
updateDoc.clear();
Serial.print("Update Payload: "); Serial.print("Update Payload: ");
Serial.println(updatePayload); Serial.println(updatePayload);
@@ -365,7 +434,7 @@ bool updateSpoolBambuData(String payload) {
6144, // Stackgröße in Bytes 6144, // Stackgröße in Bytes
(void*)params, // Parameter (void*)params, // Parameter
0, // Priorität 0, // Priorität
NULL // Task-Handle (nicht benötigt) apiTask // Task-Handle (nicht benötigt)
); );
return true; return true;
@@ -373,247 +442,264 @@ bool updateSpoolBambuData(String payload) {
// #### Spoolman init // #### Spoolman init
bool checkSpoolmanExtraFields() { bool checkSpoolmanExtraFields() {
HTTPClient http; // Only check extra fields if they have not been checked before
String checkUrls[] = { if(!spoolmanExtraFieldsChecked){
spoolmanUrl + apiUrl + "/field/spool", HTTPClient http;
spoolmanUrl + apiUrl + "/field/filament" String checkUrls[] = {
}; spoolmanUrl + apiUrl + "/field/spool",
spoolmanUrl + apiUrl + "/field/filament"
};
String spoolExtra[] = { String spoolExtra[] = {
"nfc_id" "nfc_id"
}; };
String filamentExtra[] = { String filamentExtra[] = {
"nozzle_temperature", "nozzle_temperature",
"price_meter", "price_meter",
"price_gramm", "price_gramm",
"bambu_setting_id", "bambu_setting_id",
"bambu_cali_id", "bambu_cali_id",
"bambu_idx", "bambu_idx",
"bambu_k", "bambu_k",
"bambu_flow_ratio", "bambu_flow_ratio",
"bambu_max_volspeed" "bambu_max_volspeed"
}; };
String spoolExtraFields[] = { String spoolExtraFields[] = {
"{\"name\": \"NFC ID\"," "{\"name\": \"NFC ID\","
"\"key\": \"nfc_id\"," "\"key\": \"nfc_id\","
"\"field_type\": \"text\"}" "\"field_type\": \"text\"}"
}; };
String filamentExtraFields[] = { String filamentExtraFields[] = {
"{\"name\": \"Nozzle Temp\"," "{\"name\": \"Nozzle Temp\","
"\"unit\": \"°C\"," "\"unit\": \"°C\","
"\"field_type\": \"integer_range\"," "\"field_type\": \"integer_range\","
"\"default_value\": \"[190,230]\"," "\"default_value\": \"[190,230]\","
"\"key\": \"nozzle_temperature\"}", "\"key\": \"nozzle_temperature\"}",
"{\"name\": \"Price/m\"," "{\"name\": \"Price/m\","
"\"unit\": \"\"," "\"unit\": \"\","
"\"field_type\": \"float\"," "\"field_type\": \"float\","
"\"key\": \"price_meter\"}", "\"key\": \"price_meter\"}",
"{\"name\": \"Price/g\"," "{\"name\": \"Price/g\","
"\"unit\": \"\"," "\"unit\": \"\","
"\"field_type\": \"float\"," "\"field_type\": \"float\","
"\"key\": \"price_gramm\"}", "\"key\": \"price_gramm\"}",
"{\"name\": \"Bambu Setting ID\"," "{\"name\": \"Bambu Setting ID\","
"\"field_type\": \"text\"," "\"field_type\": \"text\","
"\"key\": \"bambu_setting_id\"}", "\"key\": \"bambu_setting_id\"}",
"{\"name\": \"Bambu Cali ID\"," "{\"name\": \"Bambu Cali ID\","
"\"field_type\": \"text\"," "\"field_type\": \"text\","
"\"key\": \"bambu_cali_id\"}", "\"key\": \"bambu_cali_id\"}",
"{\"name\": \"Bambu Filament IDX\"," "{\"name\": \"Bambu Filament IDX\","
"\"field_type\": \"text\"," "\"field_type\": \"text\","
"\"key\": \"bambu_idx\"}", "\"key\": \"bambu_idx\"}",
"{\"name\": \"Bambu k\"," "{\"name\": \"Bambu k\","
"\"field_type\": \"float\"," "\"field_type\": \"float\","
"\"key\": \"bambu_k\"}", "\"key\": \"bambu_k\"}",
"{\"name\": \"Bambu Flow Ratio\"," "{\"name\": \"Bambu Flow Ratio\","
"\"field_type\": \"float\"," "\"field_type\": \"float\","
"\"key\": \"bambu_flow_ratio\"}", "\"key\": \"bambu_flow_ratio\"}",
"{\"name\": \"Bambu Max Vol. Speed\"," "{\"name\": \"Bambu Max Vol. Speed\","
"\"unit\": \"mm3/s\"," "\"unit\": \"mm3/s\","
"\"field_type\": \"integer\"," "\"field_type\": \"integer\","
"\"default_value\": \"12\"," "\"default_value\": \"12\","
"\"key\": \"bambu_max_volspeed\"}" "\"key\": \"bambu_max_volspeed\"}"
}; };
Serial.println("Überprüfe Extrafelder..."); Serial.println("Überprüfe Extrafelder...");
int urlLength = sizeof(checkUrls) / sizeof(checkUrls[0]); int urlLength = sizeof(checkUrls) / sizeof(checkUrls[0]);
for (uint8_t i = 0; i < urlLength; i++) { for (uint8_t i = 0; i < urlLength; i++) {
Serial.println(); Serial.println();
Serial.println("-------- Prüfe Felder für "+checkUrls[i]+" --------"); Serial.println("-------- Prüfe Felder für "+checkUrls[i]+" --------");
http.begin(checkUrls[i]); http.begin(checkUrls[i]);
int httpCode = http.GET(); int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) { if (httpCode == HTTP_CODE_OK) {
String payload = http.getString(); String payload = http.getString();
JsonDocument doc; JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload); DeserializationError error = deserializeJson(doc, payload);
if (!error) { if (!error) {
String* extraFields; String* extraFields;
String* extraFieldData; String* extraFieldData;
u16_t extraLength; u16_t extraLength;
if (i == 0) { if (i == 0) {
extraFields = spoolExtra; extraFields = spoolExtra;
extraFieldData = spoolExtraFields; extraFieldData = spoolExtraFields;
extraLength = sizeof(spoolExtra) / sizeof(spoolExtra[0]); extraLength = sizeof(spoolExtra) / sizeof(spoolExtra[0]);
} else { } else {
extraFields = filamentExtra; extraFields = filamentExtra;
extraFieldData = filamentExtraFields; extraFieldData = filamentExtraFields;
extraLength = sizeof(filamentExtra) / sizeof(filamentExtra[0]); extraLength = sizeof(filamentExtra) / sizeof(filamentExtra[0]);
}
for (uint8_t s = 0; s < extraLength; s++) {
bool found = false;
for (JsonObject field : doc.as<JsonArray>()) {
if (field["key"].is<String>() && field["key"] == extraFields[s]) {
Serial.println("Feld gefunden: " + extraFields[s]);
found = true;
break;
}
} }
if (!found) {
Serial.println("Feld nicht gefunden: " + extraFields[s]);
// Extrafeld hinzufügen for (uint8_t s = 0; s < extraLength; s++) {
http.begin(checkUrls[i] + "/" + extraFields[s]); bool found = false;
http.addHeader("Content-Type", "application/json"); for (JsonObject field : doc.as<JsonArray>()) {
int httpCode = http.POST(extraFieldData[s]); if (field["key"].is<String>() && field["key"] == extraFields[s]) {
Serial.println("Feld gefunden: " + extraFields[s]);
found = true;
break;
}
}
if (!found) {
Serial.println("Feld nicht gefunden: " + extraFields[s]);
if (httpCode > 0) { // Extrafeld hinzufügen
// Antwortscode und -nachricht abrufen http.begin(checkUrls[i] + "/" + extraFields[s]);
String response = http.getString(); http.addHeader("Content-Type", "application/json");
//Serial.println("HTTP-Code: " + String(httpCode)); int httpCode = http.POST(extraFieldData[s]);
//Serial.println("Antwort: " + response);
if (httpCode != HTTP_CODE_OK) {
if (httpCode > 0) {
// Antwortscode und -nachricht abrufen
String response = http.getString();
//Serial.println("HTTP-Code: " + String(httpCode));
//Serial.println("Antwort: " + response);
if (httpCode != HTTP_CODE_OK) {
return false;
}
} else {
// Fehler beim Senden der Anfrage
Serial.println("Fehler beim Senden der Anfrage: " + String(http.errorToString(httpCode)));
return false; return false;
} }
} else { //http.end();
// Fehler beim Senden der Anfrage
Serial.println("Fehler beim Senden der Anfrage: " + String(http.errorToString(httpCode)));
return false;
} }
//http.end(); yield();
vTaskDelay(100 / portTICK_PERIOD_MS);
} }
yield();
vTaskDelay(100 / portTICK_PERIOD_MS);
} }
doc.clear();
} }
} }
Serial.println("-------- ENDE Prüfe Felder --------");
Serial.println();
http.end();
spoolmanExtraFieldsChecked = true;
return true;
}else{
return true;
} }
Serial.println("-------- ENDE Prüfe Felder --------");
Serial.println();
http.end();
return true;
} }
bool checkSpoolmanInstance(const String& url) { bool checkSpoolmanInstance() {
HTTPClient http; HTTPClient http;
String healthUrl = url + apiUrl + "/health"; bool returnValue = false;
Serial.print("Überprüfe Spoolman-Instanz unter: "); // Only do the spoolman instance check if there is no active API request going on
Serial.println(healthUrl); if(spoolmanApiState == API_IDLE){
spoolmanApiState = API_TRANSMITTING;
String healthUrl = spoolmanUrl + apiUrl + "/health";
http.begin(healthUrl); Serial.print("Checking spoolman instance: ");
int httpCode = http.GET(); Serial.println(healthUrl);
if (httpCode > 0) { http.begin(healthUrl);
if (httpCode == HTTP_CODE_OK) { int httpCode = http.GET();
oledShowMessage("Spoolman available");
vTaskDelay(1000 / portTICK_PERIOD_MS);
String payload = http.getString(); if (httpCode > 0) {
JsonDocument doc; if (httpCode == HTTP_CODE_OK) {
DeserializationError error = deserializeJson(doc, payload); String payload = http.getString();
if (!error && doc["status"].is<String>()) { JsonDocument doc;
const char* status = doc["status"]; DeserializationError error = deserializeJson(doc, payload);
http.end(); if (!error && doc["status"].is<String>()) {
const char* status = doc["status"];
http.end();
if (!checkSpoolmanExtraFields()) { if (!checkSpoolmanExtraFields()) {
Serial.println("Fehler beim Überprüfen der Extrafelder."); Serial.println("Fehler beim Überprüfen der Extrafelder.");
oledShowMessage("Spoolman Error creating Extrafields"); // TBD
vTaskDelay(2000 / portTICK_PERIOD_MS); oledShowMessage("Spoolman Error creating Extrafields");
vTaskDelay(2000 / portTICK_PERIOD_MS);
return false; return false;
}
spoolmanApiState = API_IDLE;
oledShowTopRow();
spoolmanConnected = true;
returnValue = strcmp(status, "healthy") == 0;
}else{
spoolmanConnected = false;
} }
spoolmanApiState = API_IDLE; doc.clear();
oledShowTopRow(); }else{
return strcmp(status, "healthy") == 0; spoolmanConnected = false;
} }
} else {
spoolmanConnected = false;
Serial.println("Error contacting spoolman instance! HTTP Code: " + String(httpCode));
} }
} else { http.end();
Serial.println("Error contacting spoolman instance! HTTP Code: " + String(httpCode)); returnValue = false;
spoolmanApiState = API_IDLE;
}else{
// If the check is skipped, return the previous status
Serial.println("Skipping spoolman healthcheck, API is active.");
returnValue = spoolmanConnected;
} }
http.end(); Serial.println("Healthcheck completed!");
return false; return returnValue;
} }
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk) { bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, const String& octoTk) {
if (!checkSpoolmanInstance(url)) return false; Preferences preferences;
preferences.begin(NVS_NAMESPACE_API, false); // false = readwrite
preferences.putString(NVS_KEY_SPOOLMAN_URL, url);
preferences.putBool(NVS_KEY_OCTOPRINT_ENABLED, octoOn);
preferences.putString(NVS_KEY_OCTOPRINT_URL, octo_url);
preferences.putString(NVS_KEY_OCTOPRINT_TOKEN, octoTk);
preferences.end();
JsonDocument doc; //TBD: This could be handled nicer in the future
doc["url"] = url; spoolmanExtraFieldsChecked = false;
doc["octoEnabled"] = octoOn;
doc["octoUrl"] = octoWh;
doc["octoToken"] = octoTk;
Serial.print("Speichere Spoolman Data in Datei: ");
Serial.println(doc.as<String>());
if (!saveJsonValue("/spoolman_url.json", doc)) {
Serial.println("Fehler beim Speichern der Spoolman-URL.");
return false;
}
spoolmanUrl = url; spoolmanUrl = url;
octoEnabled = octoOn; octoEnabled = octoOn;
octoUrl = octoWh; octoUrl = octo_url;
octoToken = octoTk; octoToken = octoTk;
return true; return checkSpoolmanInstance();
} }
String loadSpoolmanUrl() { String loadSpoolmanUrl() {
JsonDocument doc; Preferences preferences;
if (loadJsonValue("/spoolman_url.json", doc) && doc["url"].is<String>()) { preferences.begin(NVS_NAMESPACE_API, true);
octoEnabled = (doc["octoEnabled"].is<bool>()) ? doc["octoEnabled"].as<bool>() : false; String spoolmanUrl = preferences.getString(NVS_KEY_SPOOLMAN_URL, "");
if (octoEnabled && doc["octoToken"].is<String>() && doc["octoUrl"].is<String>()) octoEnabled = preferences.getBool(NVS_KEY_OCTOPRINT_ENABLED, false);
{ if(octoEnabled)
octoUrl = doc["octoUrl"].as<String>(); {
octoToken = doc["octoToken"].as<String>(); octoUrl = preferences.getString(NVS_KEY_OCTOPRINT_URL, "");
} octoToken = preferences.getString(NVS_KEY_OCTOPRINT_TOKEN, "");
return doc["url"].as<String>();
} }
Serial.println("Keine gültige Spoolman-URL gefunden."); preferences.end();
return ""; return spoolmanUrl;
} }
bool initSpoolman() { bool initSpoolman() {
oledShowProgressBar(3, 7, DISPLAY_BOOT_TEXT, "Spoolman init");
spoolmanUrl = loadSpoolmanUrl(); spoolmanUrl = loadSpoolmanUrl();
spoolmanUrl.trim();
if (spoolmanUrl == "") {
Serial.println("Keine Spoolman-URL gefunden.");
return false;
}
bool success = checkSpoolmanInstance(spoolmanUrl); bool success = checkSpoolmanInstance();
if (!success) { if (!success) {
Serial.println("Spoolman nicht erreichbar."); Serial.println("Spoolman not available");
return false; return false;
} }

View File

@@ -24,10 +24,12 @@ extern volatile spoolmanApiStateType spoolmanApiState;
extern bool spoolman_connected; extern bool spoolman_connected;
extern String spoolmanUrl; extern String spoolmanUrl;
extern bool octoEnabled; extern bool octoEnabled;
extern bool sendOctoUpdate;
extern String octoUrl; extern String octoUrl;
extern String octoToken; extern String octoToken;
extern bool spoolmanConnected;
bool checkSpoolmanInstance(const String& url); bool checkSpoolmanInstance();
bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk); bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octoWh, const String& octoTk);
String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL String loadSpoolmanUrl(); // Neue Funktion zum Laden der URL
bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder bool checkSpoolmanExtraFields(); // Neue Funktion zum Überprüfen der Extrafelder

View File

@@ -10,6 +10,7 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "config.h" #include "config.h"
#include "display.h" #include "display.h"
#include <Preferences.h>
WiFiClient espClient; WiFiClient espClient;
SSLClient sslClient(&espClient); SSLClient sslClient(&espClient);
@@ -17,22 +18,13 @@ PubSubClient client(sslClient);
TaskHandle_t BambuMqttTask; TaskHandle_t BambuMqttTask;
String topic = "";
//String request_topic = "";
const char* bambu_username = "bblp";
const char* bambu_ip = nullptr;
const char* bambu_accesscode = nullptr;
const char* bambu_serialnr = nullptr;
String g_bambu_ip = "";
String g_bambu_accesscode = "";
String g_bambu_serialnr = "";
bool bambuDisabled = false; bool bambuDisabled = false;
bool bambu_connected = false; bool bambu_connected = false;
bool autoSendToBambu = false;
int autoSetToBambuSpoolId = 0; int autoSetToBambuSpoolId = 0;
BambuCredentials bambuCredentials;
// Globale Variablen für AMS-Daten // Globale Variablen für AMS-Daten
int ams_count = 0; int ams_count = 0;
String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients String amsJsonData; // Speichert das fertige JSON für WebSocket-Clients
@@ -43,18 +35,22 @@ bool removeBambuCredentials() {
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
} }
if (!removeJsonValue("/bambu_credentials.json")) { Preferences preferences;
Serial.println("Fehler beim Löschen der Bambu-Credentials."); preferences.begin(NVS_NAMESPACE_BAMBU, false); // false = readwrite
return false; preferences.remove(NVS_KEY_BAMBU_IP);
} preferences.remove(NVS_KEY_BAMBU_SERIAL);
preferences.remove(NVS_KEY_BAMBU_ACCESSCODE);
preferences.remove(NVS_KEY_BAMBU_AUTOSEND_ENABLE);
preferences.remove(NVS_KEY_BAMBU_AUTOSEND_TIME);
preferences.end();
// Löschen der globalen Variablen // Löschen der globalen Variablen
g_bambu_ip = ""; bambuCredentials.ip = "";
g_bambu_accesscode = ""; bambuCredentials.serial = "";
g_bambu_serialnr = ""; bambuCredentials.accesscode = "";
bambu_ip = nullptr; bambuCredentials.autosend_enable = false;
bambu_accesscode = nullptr; bambuCredentials.autosend_time = BAMBU_DEFAULT_AUTOSEND_TIME;
bambu_serialnr = nullptr;
autoSendToBambu = false;
autoSetToBambuSpoolId = 0; autoSetToBambuSpoolId = 0;
ams_count = 0; ams_count = 0;
amsJsonData = ""; amsJsonData = "";
@@ -69,24 +65,20 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
vTaskDelete(BambuMqttTask); vTaskDelete(BambuMqttTask);
} }
JsonDocument doc; bambuCredentials.ip = ip.c_str();
doc["bambu_ip"] = ip; bambuCredentials.serial = serialnr.c_str();
doc["bambu_accesscode"] = accesscode; bambuCredentials.accesscode = accesscode.c_str();
doc["bambu_serialnr"] = serialnr; bambuCredentials.autosend_enable = autoSend;
doc["autoSendToBambu"] = autoSend; bambuCredentials.autosend_time = autoSendTime.toInt();
doc["autoSendTime"] = (autoSendTime != "") ? autoSendTime.toInt() : autoSetBambuAmsCounter;
if (!saveJsonValue("/bambu_credentials.json", doc)) { Preferences preferences;
Serial.println("Fehler beim Speichern der Bambu-Credentials."); preferences.begin(NVS_NAMESPACE_BAMBU, false); // false = readwrite
return false; preferences.putString(NVS_KEY_BAMBU_IP, bambuCredentials.ip);
} preferences.putString(NVS_KEY_BAMBU_SERIAL, bambuCredentials.serial);
preferences.putString(NVS_KEY_BAMBU_ACCESSCODE, bambuCredentials.accesscode);
// Dynamische Speicherallokation für die globalen Pointer preferences.putBool(NVS_KEY_BAMBU_AUTOSEND_ENABLE, bambuCredentials.autosend_enable);
bambu_ip = ip.c_str(); preferences.putInt(NVS_KEY_BAMBU_AUTOSEND_TIME, bambuCredentials.autosend_time);
bambu_accesscode = accesscode.c_str(); preferences.end();
bambu_serialnr = serialnr.c_str();
autoSendToBambu = autoSend;
autoSetBambuAmsCounter = autoSendTime.toInt();
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
if (!setupMqtt()) return false; if (!setupMqtt()) return false;
@@ -95,35 +87,36 @@ bool saveBambuCredentials(const String& ip, const String& serialnr, const String
} }
bool loadBambuCredentials() { bool loadBambuCredentials() {
JsonDocument doc; Preferences preferences;
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) { preferences.begin(NVS_NAMESPACE_BAMBU, true);
// Temporäre Strings für die Werte String ip = preferences.getString(NVS_KEY_BAMBU_IP, "");
String ip = doc["bambu_ip"].as<String>(); String serial = preferences.getString(NVS_KEY_BAMBU_SERIAL, "");
String code = doc["bambu_accesscode"].as<String>(); String code = preferences.getString(NVS_KEY_BAMBU_ACCESSCODE, "");
String serial = doc["bambu_serialnr"].as<String>(); bool autosendEnable = preferences.getBool(NVS_KEY_BAMBU_AUTOSEND_ENABLE, false);
int autosendTime = preferences.getInt(NVS_KEY_BAMBU_AUTOSEND_TIME, BAMBU_DEFAULT_AUTOSEND_TIME);
preferences.end();
g_bambu_ip = ip; if(ip != ""){
g_bambu_accesscode = code; bambuCredentials.ip = ip.c_str();
g_bambu_serialnr = serial; bambuCredentials.serial = serial.c_str();
bambuCredentials.accesscode = code.c_str();
bambuCredentials.autosend_enable = autosendEnable;
bambuCredentials.autosend_time = autosendTime;
if (doc["autoSendToBambu"].is<bool>()) autoSendToBambu = doc["autoSendToBambu"].as<bool>(); Serial.println("credentials loaded loadCredentials!");
if (doc["autoSendTime"].is<int>()) autoSetBambuAmsCounter = doc["autoSendTime"].as<int>(); Serial.println(bambuCredentials.ip);
Serial.println(bambuCredentials.serial);
Serial.println(bambuCredentials.accesscode);
Serial.println(String(bambuCredentials.autosend_enable));
Serial.println(String(bambuCredentials.autosend_time));
ip.trim();
code.trim();
serial.trim();
// Dynamische Speicherallokation für die globalen Pointer
bambu_ip = g_bambu_ip.c_str();
bambu_accesscode = g_bambu_accesscode.c_str();
bambu_serialnr = g_bambu_serialnr.c_str();
topic = "device/" + String(bambu_serialnr);
//request_topic = "device/" + String(bambu_serialnr) + "/request";
return true; return true;
} }
Serial.println("Keine gültigen Bambu-Credentials gefunden."); else
return false; {
Serial.println("Keine gültigen Bambu-Credentials gefunden.");
return false;
}
} }
struct FilamentResult { struct FilamentResult {
@@ -226,7 +219,7 @@ FilamentResult findFilamentIdx(String brand, String type) {
bool sendMqttMessage(const String& payload) { bool sendMqttMessage(const String& payload) {
Serial.println("Sending MQTT message"); Serial.println("Sending MQTT message");
Serial.println(payload); Serial.println(payload);
if (client.publish((String(topic) + "/request").c_str(), payload.c_str())) if (client.publish(("device/"+bambuCredentials.serial+"/request").c_str(), payload.c_str()))
{ {
return true; return true;
} }
@@ -499,7 +492,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) { trayObj["cali_idx"].as<String>() != ams_data[storedIndex].trays[j].cali_idx) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
{ {
autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id); autoSetSpool(autoSetToBambuSpoolId, ams_data[storedIndex].trays[j].id);
} }
@@ -523,7 +516,7 @@ void mqtt_callback(char* topic, byte* payload, unsigned int length) {
(vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) { (vtTray["tray_type"].as<String>() != "" && vtTray["cali_idx"].as<String>() != ams_data[i].trays[0].cali_idx)) {
hasChanges = true; hasChanges = true;
if (autoSendToBambu && autoSetToBambuSpoolId > 0 && hasChanges) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0 && hasChanges)
{ {
autoSetSpool(autoSetToBambuSpoolId, 254); autoSetSpool(autoSetToBambuSpoolId, 254);
} }
@@ -580,11 +573,11 @@ void reconnect() {
oledShowTopRow(); oledShowTopRow();
// Attempt to connect // Attempt to connect
String clientId = String(bambu_serialnr) + "_" + String(random(0, 100)); String clientId = bambuCredentials.serial + "_" + String(random(0, 100));
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode)) { if (client.connect(clientId.c_str(), BAMBU_USERNAME, bambuCredentials.accesscode.c_str())) {
Serial.println("MQTT re/connected"); Serial.println("MQTT re/connected");
client.subscribe((String(topic) + "/report").c_str()); client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
bambu_connected = true; bambu_connected = true;
oledShowTopRow(); oledShowTopRow();
} else { } else {
@@ -630,28 +623,24 @@ void mqtt_loop(void * parameter) {
bool setupMqtt() { bool setupMqtt() {
// Wenn Bambu Daten vorhanden // Wenn Bambu Daten vorhanden
bool success = loadBambuCredentials(); //bool success = loadBambuCredentials();
if (!success) { if (bambuCredentials.ip != "" && bambuCredentials.accesscode != "" && bambuCredentials.serial != "")
bambuDisabled = true;
return false;
}
if (success && bambu_ip != "" && bambu_accesscode != "" && bambu_serialnr != "")
{ {
oledShowProgressBar(4, 7, DISPLAY_BOOT_TEXT, "Bambu init");
bambuDisabled = false; bambuDisabled = false;
sslClient.setCACert(root_ca); sslClient.setCACert(root_ca);
sslClient.setInsecure(); sslClient.setInsecure();
client.setServer(bambu_ip, 8883); client.setServer(bambuCredentials.ip.c_str(), 8883);
// Verbinden mit dem MQTT-Server // Verbinden mit dem MQTT-Server
bool connected = true; bool connected = true;
String clientId = String(bambu_serialnr) + "_" + String(random(0, 100)); String clientId = String(bambuCredentials.serial) + "_" + String(random(0, 100));
if (client.connect(clientId.c_str(), bambu_username, bambu_accesscode)) if (client.connect(bambuCredentials.ip.c_str(), BAMBU_USERNAME, bambuCredentials.accesscode.c_str()))
{ {
client.setCallback(mqtt_callback); client.setCallback(mqtt_callback);
client.setBufferSize(15488); client.setBufferSize(15488);
client.subscribe((String(topic) + "/report").c_str()); client.subscribe(("device/"+bambuCredentials.serial+"/report").c_str());
Serial.println("MQTT-Client initialisiert"); Serial.println("MQTT-Client initialisiert");
oledShowMessage("Bambu Connected"); oledShowMessage("Bambu Connected");

View File

@@ -16,6 +16,14 @@ struct TrayData {
String cali_idx; String cali_idx;
}; };
struct BambuCredentials {
String ip;
String serial;
String accesscode;
bool autosend_enable;
int autosend_time;
};
#define MAX_AMS 17 // 16 normale AMS + 1 externe Spule #define MAX_AMS 17 // 16 normale AMS + 1 externe Spule
extern String amsJsonData; // Für die vorbereiteten JSON-Daten extern String amsJsonData; // Für die vorbereiteten JSON-Daten
@@ -28,9 +36,10 @@ extern bool bambu_connected;
extern int ams_count; extern int ams_count;
extern AMSData ams_data[MAX_AMS]; extern AMSData ams_data[MAX_AMS];
extern bool autoSendToBambu; //extern bool autoSendToBambu;
extern int autoSetToBambuSpoolId; extern int autoSetToBambuSpoolId;
extern bool bambuDisabled; extern bool bambuDisabled;
extern BambuCredentials bambuCredentials;
bool removeBambuCredentials(); bool removeBambuCredentials();
bool loadBambuCredentials(); bool loadBambuCredentials();

View File

@@ -16,7 +16,6 @@ const uint8_t LOADCELL_DOUT_PIN = 16; //16;
const uint8_t LOADCELL_SCK_PIN = 17; //17; const uint8_t LOADCELL_SCK_PIN = 17; //17;
const uint8_t calVal_eepromAdress = 0; const uint8_t calVal_eepromAdress = 0;
const uint16_t SCALE_LEVEL_WEIGHT = 500; const uint16_t SCALE_LEVEL_WEIGHT = 500;
uint16_t defaultScaleCalibrationValue = 430;
// ***** HX711 // ***** HX711
// ***** TTP223 (Touch Sensor) // ***** TTP223 (Touch Sensor)
@@ -26,16 +25,11 @@ const uint8_t TTP223_PIN = 25;
// ***** Display // ***** Display
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// On an ESP32: 21(SDA), 22(SCL)
const int8_t OLED_RESET = -1; // Reset pin # (or -1 if sharing Arduino reset pin)
const uint8_t SCREEN_ADDRESS = 0x3C; ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
const uint8_t SCREEN_WIDTH = 128; // OLED display width, in pixels
const uint8_t SCREEN_HEIGHT = 64; // OLED display height, in pixels
const uint8_t OLED_TOP_START = 0; const uint8_t OLED_TOP_START = 0;
const uint8_t OLED_TOP_END = 16; const uint8_t OLED_TOP_END = 16;
const uint8_t OLED_DATA_START = 17; const uint8_t OLED_DATA_START = 17;
const uint8_t OLED_DATA_END = SCREEN_HEIGHT; const uint8_t OLED_DATA_END = SCREEN_HEIGHT;
// ***** Display // ***** Display
// ***** Webserver // ***** Webserver
@@ -46,8 +40,6 @@ const uint8_t webserverPort = 80;
const char* apiUrl = "/api/v1"; const char* apiUrl = "/api/v1";
// ***** API // ***** API
// ***** Bambu Auto Set Spool
uint8_t autoSetBambuAmsCounter = 60;
// ***** Bambu Auto Set Spool // ***** Bambu Auto Set Spool
// ***** Task Prios // ***** Task Prios

View File

@@ -3,6 +3,40 @@
#include <Arduino.h> #include <Arduino.h>
#define BAMBU_DEFAULT_AUTOSEND_TIME 60
#define NVS_NAMESPACE_API "api"
#define NVS_KEY_SPOOLMAN_URL "spoolmanUrl"
#define NVS_KEY_OCTOPRINT_ENABLED "octoEnabled"
#define NVS_KEY_OCTOPRINT_URL "octoUrl"
#define NVS_KEY_OCTOPRINT_TOKEN "octoToken"
#define NVS_NAMESPACE_BAMBU "bambu"
#define NVS_KEY_BAMBU_IP "bambuIp"
#define NVS_KEY_BAMBU_ACCESSCODE "bambuCode"
#define NVS_KEY_BAMBU_SERIAL "bambuSerial"
#define NVS_KEY_BAMBU_AUTOSEND_ENABLE "autosendEnable"
#define NVS_KEY_BAMBU_AUTOSEND_TIME "autosendTime"
#define NVS_NAMESPACE_SCALE "scale"
#define NVS_KEY_CALIBRATION "cal_value"
#define NVS_KEY_AUTOTARE "auto_tare"
#define SCALE_DEFAULT_CALIBRATION_VALUE 430.0f;
#define BAMBU_USERNAME "bblp"
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3CU // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_WIDTH 128U
#define SCREEN_HEIGHT 64U
#define SCREEN_TOP_BAR_HEIGHT 16U
#define SCREEN_PROGRESS_BAR_HEIGHT 12U
#define DISPLAY_BOOT_TEXT "FilaMan"
#define WIFI_CHECK_INTERVAL 60000U
#define DISPLAY_UPDATE_INTERVAL 1000U
#define SPOOLMAN_HEALTHCHECK_INTERVAL 60000U
extern const uint8_t PN532_IRQ; extern const uint8_t PN532_IRQ;
extern const uint8_t PN532_RESET; extern const uint8_t PN532_RESET;
@@ -13,10 +47,6 @@ extern const uint16_t SCALE_LEVEL_WEIGHT;
extern const uint8_t TTP223_PIN; extern const uint8_t TTP223_PIN;
extern const int8_t OLED_RESET;
extern const uint8_t SCREEN_ADDRESS;
extern const uint8_t SCREEN_WIDTH;
extern const uint8_t SCREEN_HEIGHT;
extern const uint8_t OLED_TOP_START; extern const uint8_t OLED_TOP_START;
extern const uint8_t OLED_TOP_END; extern const uint8_t OLED_TOP_END;
extern const uint8_t OLED_DATA_START; extern const uint8_t OLED_DATA_START;
@@ -25,7 +55,7 @@ extern const uint8_t OLED_DATA_END;
extern const char* apiUrl; extern const char* apiUrl;
extern const uint8_t webserverPort; extern const uint8_t webserverPort;
extern uint8_t autoSetBambuAmsCounter;
extern const unsigned char wifi_on[]; extern const unsigned char wifi_on[];
extern const unsigned char wifi_off[]; extern const unsigned char wifi_off[];

12
src/debug.h Normal file
View File

@@ -0,0 +1,12 @@
#include <Arduino.h>
#ifdef ENABLE_HEAP_DEBUGGING
#define HEAP_DEBUG_MESSAGE(location) printHeapDebugData(location);
#else
#define HEAP_DEBUG_MESSAGE(location)
#endif
inline void printHeapDebugData(const char *location){
Serial.println("Heap: " + String(ESP.getMinFreeHeap()/1024) + "\t" + String(ESP.getFreeHeap()/1024) + "\t" + String(ESP.getMaxAllocHeap()/1024) + "\t" + location);
}

View File

@@ -2,10 +2,12 @@
#include "api.h" #include "api.h"
#include <vector> #include <vector>
#include "icons.h" #include "icons.h"
#include "main.h"
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
bool wifiOn = false; bool wifiOn = false;
bool iconToggle = false;
void setupDisplay() { void setupDisplay() {
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
@@ -14,15 +16,10 @@ void setupDisplay() {
} }
display.setTextColor(WHITE); display.setTextColor(WHITE);
display.clearDisplay(); display.clearDisplay();
display.display();
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.setTextColor(WHITE);
display.display();
oledShowTopRow(); oledShowTopRow();
oledShowMessage("FilaMan v" + String(VERSION)); oledShowProgressBar(0, 7, DISPLAY_BOOT_TEXT, "Display init");
vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
void oledclearline() { void oledclearline() {
@@ -45,14 +42,14 @@ void oledcleardata() {
//display.display(); //display.display();
} }
int oled_center_h(String text) { int oled_center_h(const String &text) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h); display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
return (SCREEN_WIDTH - w) / 2; return (SCREEN_WIDTH - w) / 2;
} }
int oled_center_v(String text) { int oled_center_v(const String &text) {
int16_t x1, y1; int16_t x1, y1;
uint16_t w, h; uint16_t w, h;
display.getTextBounds(text, 0, OLED_DATA_START, &x1, &y1, &w, &h); display.getTextBounds(text, 0, OLED_DATA_START, &x1, &y1, &w, &h);
@@ -60,7 +57,7 @@ int oled_center_v(String text) {
return OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - h) / 2); return OLED_DATA_START + ((OLED_DATA_END - OLED_DATA_START - h) / 2);
} }
std::vector<String> splitTextIntoLines(String text, uint8_t textSize) { std::vector<String> splitTextIntoLines(const String &text, uint8_t textSize) {
std::vector<String> lines; std::vector<String> lines;
display.setTextSize(textSize); display.setTextSize(textSize);
@@ -120,7 +117,7 @@ std::vector<String> splitTextIntoLines(String text, uint8_t textSize) {
return lines; return lines;
} }
void oledShowMultilineMessage(String message, uint8_t size) { void oledShowMultilineMessage(const String &message, uint8_t size) {
std::vector<String> lines; std::vector<String> lines;
int maxLines = 3; // Maximale Anzahl Zeilen für size 2 int maxLines = 3; // Maximale Anzahl Zeilen für size 2
@@ -148,7 +145,7 @@ void oledShowMultilineMessage(String message, uint8_t size) {
display.display(); display.display();
} }
void oledShowMessage(String message, uint8_t size) { void oledShowMessage(const String &message, uint8_t size) {
oledcleardata(); oledcleardata();
display.setTextSize(size); display.setTextSize(size);
display.setTextWrap(false); display.setTextWrap(false);
@@ -171,22 +168,46 @@ void oledShowMessage(String message, uint8_t size) {
void oledShowTopRow() { void oledShowTopRow() {
oledclearline(); oledclearline();
if (bambu_connected == 1) { display.setTextSize(1);
display.drawBitmap(50, 0, bitmap_bambu_on , 16, 16, WHITE); display.setCursor(0, 4);
} else { display.print("v");
display.drawBitmap(50, 0, bitmap_off , 16, 16, WHITE); display.print(VERSION);
}
if (spoolmanApiState != API_INIT) { iconToggle = !iconToggle;
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
} else {
display.drawBitmap(80, 0, bitmap_off , 16, 16, WHITE);
}
if (wifiOn == 1) { // Do not show status indicators during boot
display.drawBitmap(107, 0, wifi_on , 16, 16, WHITE); if(!booting){
} else { if(bambuDisabled == false) {
display.drawBitmap(107, 0, wifi_off , 16, 16, WHITE); if (bambu_connected == 1) {
display.drawBitmap(50, 0, bitmap_bambu_on , 16, 16, WHITE);
} else {
if(iconToggle){
display.drawBitmap(50, 0, bitmap_bambu_on , 16, 16, WHITE);
display.drawLine(50, 15, 66, 0, WHITE);
display.drawLine(51, 15, 67, 0, WHITE);
}
}
}
if (spoolmanConnected) {
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
} else {
if(iconToggle){
display.drawBitmap(80, 0, bitmap_spoolman_on , 16, 16, WHITE);
display.drawLine(80, 15, 96, 0, WHITE);
display.drawLine(81, 15, 97, 0, WHITE);
}
}
if (wifiOn == 1) {
display.drawBitmap(107, 0, wifi_on , 16, 16, WHITE);
} else {
if(iconToggle){
display.drawBitmap(107, 0, wifi_on , 16, 16, WHITE);
display.drawLine(107, 15, 123, 0, WHITE);
display.drawLine(108, 15, 124, 0, WHITE);
}
}
} }
display.display(); display.display();
@@ -214,6 +235,27 @@ void oledShowIcon(const char* icon) {
display.display(); display.display();
} }
void oledShowProgressBar(const uint8_t step, const uint8_t numSteps, const char* largeText, const char* statusMessage){
assert(step <= numSteps);
// clear data and bar area
display.fillRect(0, OLED_DATA_START, SCREEN_WIDTH, SCREEN_HEIGHT-16, BLACK);
display.setTextWrap(false);
display.setTextSize(2);
display.setCursor(0, OLED_DATA_START+4);
display.print(largeText);
display.setTextSize(1);
display.setCursor(0, OLED_DATA_END-SCREEN_PROGRESS_BAR_HEIGHT-10);
display.print(statusMessage);
const int barLength = ((SCREEN_WIDTH-2)*step)/numSteps;
display.drawRoundRect(0, SCREEN_HEIGHT-SCREEN_PROGRESS_BAR_HEIGHT, SCREEN_WIDTH, 12, 6, WHITE);
display.fillRoundRect(1, SCREEN_HEIGHT-SCREEN_PROGRESS_BAR_HEIGHT+1, barLength, 10, 6, WHITE);
display.display();
}
void oledShowWeight(uint16_t weight) { void oledShowWeight(uint16_t weight) {
// Display Gewicht // Display Gewicht
oledcleardata(); oledcleardata();

View File

@@ -13,11 +13,13 @@ extern bool wifiOn;
void setupDisplay(); void setupDisplay();
void oledclearline(); void oledclearline();
void oledcleardata(); void oledcleardata();
int oled_center_h(String text); int oled_center_h(const String &text);
int oled_center_v(String text); int oled_center_v(const String &text);
void oledShowProgressBar(const uint8_t step, const uint8_t numSteps, const char* largeText, const char* statusMessage);
void oledShowWeight(uint16_t weight); void oledShowWeight(uint16_t weight);
void oledShowMessage(String message, uint8_t size = 2); void oledShowMessage(const String &message, uint8_t size = 2);
void oledShowTopRow(); void oledShowTopRow();
void oledShowIcon(const char* icon); void oledShowIcon(const char* icon);

View File

@@ -16,6 +16,7 @@
bool mainTaskWasPaused = 0; bool mainTaskWasPaused = 0;
uint8_t scaleTareCounter = 0; uint8_t scaleTareCounter = 0;
bool touchSensorConnected = false; bool touchSensorConnected = false;
bool booting = true;
// ##### SETUP ##### // ##### SETUP #####
void setup() { void setup() {
@@ -63,6 +64,7 @@ void setup() {
bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus bool panic = true; // Wenn true, löst ein WDT-Timeout einen System-Panik aus
esp_task_wdt_init(10, panic); esp_task_wdt_init(10, panic);
booting = false;
// Aktuellen Task (loopTask) zum Watchdog hinzufügen // Aktuellen Task (loopTask) zum Watchdog hinzufügen
esp_task_wdt_add(NULL); esp_task_wdt_add(NULL);
} }
@@ -95,7 +97,8 @@ int16_t lastWeight = 0;
// WIFI check variables // WIFI check variables
unsigned long lastWifiCheckTime = 0; unsigned long lastWifiCheckTime = 0;
const unsigned long wifiCheckInterval = 60000; // Überprüfe alle 60 Sekunden (60000 ms) unsigned long lastTopRowUpdateTime = 0;
unsigned long lastSpoolmanHealcheckTime = 0;
// Button debounce variables // Button debounce variables
unsigned long lastButtonPress = 0; unsigned long lastButtonPress = 0;
@@ -113,13 +116,25 @@ void loop() {
} }
// Überprüfe regelmäßig die WLAN-Verbindung // Überprüfe regelmäßig die WLAN-Verbindung
if (intervalElapsed(currentMillis, lastWifiCheckTime, wifiCheckInterval)) if (intervalElapsed(currentMillis, lastWifiCheckTime, WIFI_CHECK_INTERVAL))
{ {
checkWiFiConnection(); checkWiFiConnection();
} }
// Periodic display update
if (intervalElapsed(currentMillis, lastTopRowUpdateTime, DISPLAY_UPDATE_INTERVAL))
{
oledShowTopRow();
}
// Periodic spoolman health check
if (intervalElapsed(currentMillis, lastSpoolmanHealcheckTime, SPOOLMAN_HEALTHCHECK_INTERVAL))
{
checkSpoolmanInstance();
}
// Wenn Bambu auto set Spool aktiv // Wenn Bambu auto set Spool aktiv
if (autoSendToBambu && autoSetToBambuSpoolId > 0) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0)
{ {
if (!bambuDisabled && !bambu_connected) if (!bambuDisabled && !bambu_connected)
{ {
@@ -131,10 +146,10 @@ void loop() {
if (nfcReaderState == NFC_IDLE) if (nfcReaderState == NFC_IDLE)
{ {
lastAutoSetBambuAmsTime = currentMillis; lastAutoSetBambuAmsTime = currentMillis;
oledShowMessage("Auto Set " + String(autoSetBambuAmsCounter - autoAmsCounter) + "s"); oledShowMessage("Auto Set " + String(bambuCredentials.autosend_time - autoAmsCounter) + "s");
autoAmsCounter++; autoAmsCounter++;
if (autoAmsCounter >= autoSetBambuAmsCounter) if (autoAmsCounter >= bambuCredentials.autosend_time)
{ {
autoSetToBambuSpoolId = 0; autoSetToBambuSpoolId = 0;
autoAmsCounter = 0; autoAmsCounter = 0;
@@ -148,88 +163,92 @@ void loop() {
} }
} }
// Wenn Waage nicht Kalibriert // If scale is not calibrated, only show a warning
if (scaleCalibrated == 3) if (!scaleCalibrated)
{ {
oledShowMessage("Scale not calibrated!"); // Do not show the warning if the calibratin process is onging
vTaskDelay(5000 / portTICK_PERIOD_MS); if(!scaleCalibrationActive){
yield(); oledShowMessage("Scale not calibrated");
esp_task_wdt_reset(); vTaskDelay(1000 / portTICK_PERIOD_MS);
return;
}
// Ausgabe der Waage auf Display
if(pauseMainTask == 0)
{
if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!autoSendToBambu || autoSetToBambuSpoolId == 0)))
{
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
} }
mainTaskWasPaused = false; }else{
} // Ausgabe der Waage auf Display
else if(pauseMainTask == 0)
{
mainTaskWasPaused = true;
}
// Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState < NFC_WRITING)
{
lastWeightReadTime = currentMillis;
// Prüfen ob die Waage korrekt genullt ist
// Abweichung von 2g ignorieren
if (autoTare && (weight > 2 && weight < 7) || weight < -2)
{ {
scale_tare_counter++; if (mainTaskWasPaused || (weight != lastWeight && nfcReaderState == NFC_IDLE && (!bambuCredentials.autosend_enable || autoSetToBambuSpoolId == 0)))
{
(weight < 2) ? ((weight < -2) ? oledShowMessage("!! -0") : oledShowWeight(0)) : oledShowWeight(weight);
}
mainTaskWasPaused = false;
} }
else else
{ {
scale_tare_counter = 0; mainTaskWasPaused = true;
} }
// Prüfen ob das Gewicht gleich bleibt und dann senden
if (abs(weight - lastWeight) <= 2 && weight > 5) // Wenn Timer abgelaufen und nicht gerade ein RFID-Tag geschrieben wird
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState < NFC_WRITING)
{ {
weigthCouterToApi++; lastWeightReadTime = currentMillis;
// Prüfen ob die Waage korrekt genullt ist
// Abweichung von 2g ignorieren
if (autoTare && (weight > 2 && weight < 7) || weight < -2)
{
scale_tare_counter++;
}
else
{
scale_tare_counter = 0;
}
// Prüfen ob das Gewicht gleich bleibt und dann senden
if (abs(weight - lastWeight) <= 2 && weight > 5)
{
weigthCouterToApi++;
}
else
{
weigthCouterToApi = 0;
weightSend = 0;
}
} }
else
// reset weight counter after writing tag
// TBD: what exactly is the logic behind this?
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS)
{ {
weigthCouterToApi = 0; weigthCouterToApi = 0;
weightSend = 0;
} }
}
// reset weight counter after writing tag lastWeight = weight;
// TBD: what exactly is the logic behind this?
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS)
{
weigthCouterToApi = 0;
}
lastWeight = weight; // Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE) {
// set the current tag as processed to prevent it beeing processed again
tagProcessed = true;
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden if (updateSpoolWeight(activeSpoolId, weight))
if (spoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS) { {
oledShowIcon("loading"); weightSend = 1;
if (updateSpoolWeight(spoolId, weight))
{
oledShowIcon("success");
vTaskDelay(2000 / portTICK_PERIOD_MS);
weightSend = 1;
autoSetToBambuSpoolId = spoolId.toInt();
if (octoEnabled) }
else
{
oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
if(sendOctoUpdate && spoolmanApiState == API_IDLE){
autoSetToBambuSpoolId = activeSpoolId.toInt();
if(octoEnabled)
{ {
updateSpoolOcto(autoSetToBambuSpoolId); updateSpoolOcto(autoSetToBambuSpoolId);
} }
} sendOctoUpdate = false;
else
{
oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
} }

9
src/main.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef MAIN_H
#define MAIN_H
#include <Arduino.h>
extern bool booting;
#endif

View File

@@ -7,6 +7,8 @@
#include "api.h" #include "api.h"
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "scale.h" #include "scale.h"
#include "bambu.h"
#include "main.h"
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS); //Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET); Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
@@ -14,9 +16,18 @@ Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
TaskHandle_t RfidReaderTask; TaskHandle_t RfidReaderTask;
JsonDocument rfidData; JsonDocument rfidData;
String spoolId = ""; String activeSpoolId = "";
String lastSpoolId = "";
String nfcJsonData = ""; String nfcJsonData = "";
bool tagProcessed = false;
volatile bool pauseBambuMqttTask = false; volatile bool pauseBambuMqttTask = false;
volatile bool nfcReadingTaskSuspendRequest = false;
volatile bool nfcReadingTaskSuspendState = false;
struct NfcWriteParameterType {
bool tagType;
char* payload;
};
volatile nfcReaderStateType nfcReaderState = NFC_IDLE; volatile nfcReaderStateType nfcReaderState = NFC_IDLE;
// 0 = nicht gelesen // 0 = nicht gelesen
@@ -64,6 +75,8 @@ void payloadToJson(uint8_t *data) {
Serial.print("deserializeJson() failed: "); Serial.print("deserializeJson() failed: ");
Serial.println(error.f_str()); Serial.println(error.f_str());
} }
doc.clear();
} else { } else {
Serial.println("Kein gültiger JSON-Inhalt gefunden oder fehlerhafte Formatierung."); Serial.println("Kein gültiger JSON-Inhalt gefunden oder fehlerhafte Formatierung.");
//writeJsonToTag("{\"version\":\"1.0\",\"protocol\":\"NFC\",\"color_hex\":\"#FFFFFF\",\"type\":\"Example\",\"min_temp\":10,\"max_temp\":30,\"brand\":\"BrandName\"}"); //writeJsonToTag("{\"version\":\"1.0\",\"protocol\":\"NFC\",\"color_hex\":\"#FFFFFF\",\"type\":\"Example\",\"min_temp\":10,\"max_temp\":30,\"brand\":\"BrandName\"}");
@@ -192,6 +205,8 @@ uint8_t ntag2xx_WriteNDEF(const char *payload) {
} }
bool decodeNdefAndReturnJson(const byte* encodedMessage) { bool decodeNdefAndReturnJson(const byte* encodedMessage) {
oledShowProgressBar(1, octoEnabled?5:4, "Reading", "Decoding data");
byte typeLength = encodedMessage[3]; byte typeLength = encodedMessage[3];
byte payloadLength = encodedMessage[4]; byte payloadLength = encodedMessage[4];
@@ -215,66 +230,79 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
} }
else else
{ {
// Sende die aktualisierten AMS-Daten an alle WebSocket-Clients // If spoolman is unavailable, there is no point in continuing
Serial.println("JSON-Dokument erfolgreich verarbeitet"); if(spoolmanConnected){
Serial.println(doc.as<String>()); // Sende die aktualisierten AMS-Daten an alle WebSocket-Clients
if (doc.containsKey("sm_id") && doc["sm_id"] != "") Serial.println("JSON-Dokument erfolgreich verarbeitet");
{ Serial.println(doc.as<String>());
Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>()); if (doc["sm_id"].is<String>() && doc["sm_id"] != "")
spoolId = doc["sm_id"].as<String>(); {
} oledShowProgressBar(2, octoEnabled?5:4, "Spool Tag", "Weighing");
else if(doc.containsKey("location") && doc["location"] != "") Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>());
{ activeSpoolId = doc["sm_id"].as<String>();
Serial.println("Location Tag found!"); lastSpoolId = activeSpoolId;
String location = doc["location"].as<String>(); }
if(spoolId != ""){ else if(doc["location"].is<String>() && doc["location"] != "")
updateSpoolLocation(spoolId, location); {
Serial.println("Location Tag found!");
String location = doc["location"].as<String>();
if(lastSpoolId != ""){
updateSpoolLocation(lastSpoolId, location);
}
else
{
Serial.println("Location update tag scanned without scanning spool before!");
oledShowProgressBar(1, 1, "Failure", "Scan spool first");
}
} }
else else
{ {
Serial.println("Location update tag scanned without scanning spool before!"); Serial.println("Keine SPOOL-ID gefunden.");
oledShowMessage("No spool scanned before!"); activeSpoolId = "";
oledShowProgressBar(1, 1, "Failure", "Unkown tag");
} }
}else{
} oledShowProgressBar(octoEnabled?5:4, octoEnabled?5:4, "Failure!", "Spoolman unavailable");
else
{
Serial.println("Keine SPOOL-ID gefunden.");
spoolId = "";
oledShowMessage("Unknown Spool");
vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
} }
doc.clear();
return true; return true;
} }
void writeJsonToTag(void *parameter) { void writeJsonToTag(void *parameter) {
const char* payload = (const char*)parameter; NfcWriteParameterType* params = (NfcWriteParameterType*)parameter;
// Gib die erstellte NDEF-Message aus // Gib die erstellte NDEF-Message aus
Serial.println("Erstelle NDEF-Message..."); Serial.println("Erstelle NDEF-Message...");
Serial.println(payload); Serial.println(params->payload);
nfcReaderState = NFC_WRITING; nfcReaderState = NFC_WRITING;
vTaskSuspend(RfidReaderTask);
vTaskDelay(50 / portTICK_PERIOD_MS); // First request the reading task to be suspended and than wait until it responds
nfcReadingTaskSuspendRequest = true;
while(nfcReadingTaskSuspendState == false){
vTaskDelay(100 / portTICK_PERIOD_MS);
}
//pauseBambuMqttTask = true; //pauseBambuMqttTask = true;
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData();
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
oledShowMessage("Waiting for NFC-Tag");
// Wait 10sec for tag // Wait 10sec for tag
uint8_t success = 0; uint8_t success = 0;
String uidString = ""; String uidString = "";
for (uint16_t i = 0; i < 20; i++) { for (uint16_t i = 0; i < 20; i++) {
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;
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500); // yield before potentially waiting for 400ms
yield();
esp_task_wdt_reset();
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400);
if (success) { if (success) {
for (uint8_t i = 0; i < uidLength; i++) { for (uint8_t i = 0; i < uidLength; i++) {
//TBD: Rework to remove all the string operations
uidString += String(uid[i], HEX); uidString += String(uid[i], HEX);
if (i < uidLength - 1) { if (i < uidLength - 1) {
uidString += ":"; // Optional: Trennzeichen hinzufügen uidString += ":"; // Optional: Trennzeichen hinzufügen
@@ -284,8 +312,6 @@ void writeJsonToTag(void *parameter) {
break; break;
} }
if (i == 0) oledShowMessage("Waiting for NFC-Tag");
yield(); yield();
esp_task_wdt_reset(); esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
@@ -293,29 +319,37 @@ void writeJsonToTag(void *parameter) {
if (success) if (success)
{ {
oledShowIcon("transfer"); oledShowProgressBar(1, 3, "Write Tag", "Writing");
// Schreibe die NDEF-Message auf den Tag // Schreibe die NDEF-Message auf den Tag
success = ntag2xx_WriteNDEF(payload); success = ntag2xx_WriteNDEF(params->payload);
if (success) if (success)
{ {
Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben"); Serial.println("NDEF-Message erfolgreich auf den Tag geschrieben");
//oledShowMessage("NFC-Tag written"); //oledShowMessage("NFC-Tag written");
oledShowIcon("success"); //vTaskDelay(1000 / portTICK_PERIOD_MS);
vTaskDelay(1000 / portTICK_PERIOD_MS);
nfcReaderState = NFC_WRITE_SUCCESS; nfcReaderState = NFC_WRITE_SUCCESS;
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData();
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
if (updateSpoolTagId(uidString, payload)) { if(params->tagType){
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID // TBD: should this be simplified?
uint8_t uidLength; if (updateSpoolTagId(uidString, params->payload) && params->tagType) {
oledShowIcon("success");
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) {
yield();
}
}
}else{
// Potentially handle errors
}
}else{
oledShowProgressBar(1, 1, "Write Tag", "Done!");
}
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength;
yield();
esp_task_wdt_reset();
while (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400)) {
yield();
}
vTaskResume(RfidReaderTask); vTaskResume(RfidReaderTask);
vTaskDelay(500 / portTICK_PERIOD_MS); vTaskDelay(500 / portTICK_PERIOD_MS);
} }
@@ -330,34 +364,40 @@ void writeJsonToTag(void *parameter) {
else else
{ {
Serial.println("Fehler: Kein Tag zu schreiben gefunden."); Serial.println("Fehler: Kein Tag zu schreiben gefunden.");
oledShowMessage("No NFC-Tag found"); oledShowProgressBar(1, 1, "Failure!", "No tag found");
vTaskDelay(2000 / portTICK_PERIOD_MS); vTaskDelay(2000 / portTICK_PERIOD_MS);
nfcReaderState = NFC_IDLE; nfcReaderState = NFC_IDLE;
} }
sendWriteResult(nullptr, success); sendWriteResult(nullptr, success);
sendNfcData(nullptr); sendNfcData();
vTaskResume(RfidReaderTask); nfcReadingTaskSuspendRequest = false;
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
vTaskDelete(NULL); vTaskDelete(NULL);
} }
void startWriteJsonToTag(const char* payload) { void startWriteJsonToTag(const bool isSpoolTag, const char* payload) {
char* payloadCopy = strdup(payload); NfcWriteParameterType* parameters = new NfcWriteParameterType();
parameters->tagType = isSpoolTag;
parameters->payload = strdup(payload);
// Task nicht mehrfach starten // Task nicht mehrfach starten
if (nfcReaderState != NFC_WRITING) { if (nfcReaderState == NFC_IDLE || nfcReaderState == NFC_READ_ERROR || nfcReaderState == NFC_READ_SUCCESS) {
oledShowProgressBar(0, 1, "Write Tag", "Place tag now");
// Erstelle die Task // Erstelle die Task
xTaskCreate( xTaskCreate(
writeJsonToTag, // Task-Funktion writeJsonToTag, // Task-Funktion
"WriteJsonToTagTask", // Task-Name "WriteJsonToTagTask", // Task-Name
5115, // Stackgröße in Bytes 5115, // Stackgröße in Bytes
(void*)payloadCopy, // Parameter (void*)parameters, // Parameter
rfidWriteTaskPrio, // Priorität rfidWriteTaskPrio, // Priorität
NULL // Task-Handle (nicht benötigt) NULL // Task-Handle (nicht benötigt)
); );
}else{
oledShowProgressBar(0, 1, "FAILURE", "NFC busy!");
// TBD: Add proper error handling (website)
} }
} }
@@ -365,27 +405,33 @@ void scanRfidTask(void * parameter) {
Serial.println("RFID Task gestartet"); Serial.println("RFID Task gestartet");
for(;;) { for(;;) {
// Wenn geschrieben wird Schleife aussetzen // Wenn geschrieben wird Schleife aussetzen
if (nfcReaderState != NFC_WRITING) if (nfcReaderState != NFC_WRITING && !nfcReadingTaskSuspendRequest && !booting)
{ {
nfcReadingTaskSuspendState = false;
yield(); 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;
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000); success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500);
foundNfcTag(nullptr, success); foundNfcTag(nullptr, success);
if (success && nfcReaderState != NFC_READ_SUCCESS) // As long as there is still a tag on the reader, do not try to read it again
if (success && nfcReaderState == NFC_IDLE)
{ {
// Set the current tag as not processed
tagProcessed = false;
// Display some basic information about the card // Display some basic information about the card
Serial.println("Found an ISO14443A card"); Serial.println("Found an ISO14443A card");
nfcReaderState = NFC_READING; nfcReaderState = NFC_READING;
oledShowIcon("transfer"); oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag");
vTaskDelay(500 / portTICK_PERIOD_MS);
//vTaskDelay(500 / portTICK_PERIOD_MS);
if (uidLength == 7) if (uidLength == 7)
{ {
@@ -418,8 +464,7 @@ void scanRfidTask(void * parameter) {
if (!decodeNdefAndReturnJson(data)) if (!decodeNdefAndReturnJson(data))
{ {
oledShowMessage("NFC-Tag unknown"); oledShowProgressBar(1, 1, "Failure", "Unknown tag");
vTaskDelay(2000 / portTICK_PERIOD_MS);
nfcReaderState = NFC_READ_ERROR; nfcReaderState = NFC_READ_ERROR;
} }
else else
@@ -431,42 +476,50 @@ void scanRfidTask(void * parameter) {
} }
else else
{ {
oledShowMessage("NFC-Tag read error"); oledShowProgressBar(1, 1, "Failure", "Tag read error");
nfcReaderState = NFC_READ_ERROR; nfcReaderState = NFC_READ_ERROR;
} }
} }
else else
{ {
//TBD: Show error here?!
oledShowProgressBar(1, 1, "Failure", "Unkown tag type");
Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!"); Serial.println("This doesn't seem to be an NTAG2xx tag (UUID length != 7 bytes)!");
} }
} }
if (!success && nfcReaderState != NFC_IDLE) if (!success && nfcReaderState != NFC_IDLE && !nfcReadingTaskSuspendRequest)
{ {
nfcReaderState = NFC_IDLE; nfcReaderState = NFC_IDLE;
//uidString = ""; //uidString = "";
nfcJsonData = ""; nfcJsonData = "";
activeSpoolId = "";
Serial.println("Tag entfernt"); Serial.println("Tag entfernt");
if (!autoSendToBambu) oledShowWeight(weight); if (!bambuCredentials.autosend_enable) oledShowWeight(weight);
} }
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert
sendNfcData(nullptr); sendNfcData();
}
else
{
nfcReadingTaskSuspendState = true;
Serial.println("NFC Reading disabled");
vTaskDelay(1000 / portTICK_PERIOD_MS);
} }
yield(); yield();
} }
} }
void startNfc() { void startNfc() {
oledShowProgressBar(5, 7, DISPLAY_BOOT_TEXT, "NFC init");
nfc.begin(); // Beginne Kommunikation mit RFID Leser nfc.begin(); // Beginne Kommunikation mit RFID Leser
delay(1000); delay(1000);
unsigned long versiondata = nfc.getFirmwareVersion(); // Lese Versionsnummer der Firmware aus unsigned long versiondata = nfc.getFirmwareVersion(); // Lese Versionsnummer der Firmware aus
if (! versiondata) { // Wenn keine Antwort kommt if (! versiondata) { // Wenn keine Antwort kommt
Serial.println("Kann kein RFID Board finden !"); // Sende Text "Kann kein..." an seriellen Monitor Serial.println("Kann kein RFID Board finden !"); // Sende Text "Kann kein..." an seriellen Monitor
//delay(5000);
//ESP.restart();
oledShowMessage("No RFID Board found"); oledShowMessage("No RFID Board found");
delay(2000); vTaskDelay(2000 / portTICK_PERIOD_MS);
} }
else { else {
Serial.print("Chip PN5 gefunden"); Serial.println((versiondata >> 24) & 0xFF, HEX); // Sende Text und Versionsinfos an seriellen Serial.print("Chip PN5 gefunden"); Serial.println((versiondata >> 24) & 0xFF, HEX); // Sende Text und Versionsinfos an seriellen

View File

@@ -15,13 +15,15 @@ typedef enum{
void startNfc(); void startNfc();
void scanRfidTask(void * parameter); void scanRfidTask(void * parameter);
void startWriteJsonToTag(const char* payload); void startWriteJsonToTag(const bool isSpoolTag, const char* payload);
extern TaskHandle_t RfidReaderTask; extern TaskHandle_t RfidReaderTask;
extern String nfcJsonData; extern String nfcJsonData;
extern String spoolId; extern String activeSpoolId;
extern String lastSpoolId;
extern volatile nfcReaderStateType nfcReaderState; extern volatile nfcReaderStateType nfcReaderState;
extern volatile bool pauseBambuMqttTask; extern volatile bool pauseBambuMqttTask;
extern bool tagProcessed;

View File

@@ -224,7 +224,7 @@ void handleUpdate(AsyncWebServer &server) {
static int lastProgress = -1; static int lastProgress = -1;
if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) { if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) {
sendUpdateProgress(currentProgress, "uploading"); sendUpdateProgress(currentProgress, "uploading");
oledShowMessage("Update: " + String(currentProgress) + "%"); oledShowProgressBar(currentProgress, 100, "Update", "Download");
vTaskDelay(50 / portTICK_PERIOD_MS); vTaskDelay(50 / portTICK_PERIOD_MS);
lastProgress = currentProgress; lastProgress = currentProgress;
} }

View File

@@ -17,13 +17,9 @@ uint8_t weigthCouterToApi = 0;
uint8_t scale_tare_counter = 0; uint8_t scale_tare_counter = 0;
bool scaleTareRequest = false; bool scaleTareRequest = false;
uint8_t pauseMainTask = 0; uint8_t pauseMainTask = 0;
uint8_t scaleCalibrated = 1; bool scaleCalibrated;
Preferences preferences;
const char* NVS_NAMESPACE = "scale";
const char* NVS_KEY_CALIBRATION = "cal_value";
const char* NVS_KEY_AUTOTARE = "auto_tare";
bool autoTare = true; bool autoTare = true;
bool scaleCalibrationActive = false;
// ##### Funktionen für Waage ##### // ##### Funktionen für Waage #####
uint8_t setAutoTare(bool autoTareValue) { uint8_t setAutoTare(bool autoTareValue) {
@@ -32,7 +28,8 @@ uint8_t setAutoTare(bool autoTareValue) {
autoTare = autoTareValue; autoTare = autoTareValue;
// Speichern mit NVS // Speichern mit NVS
preferences.begin(NVS_NAMESPACE, false); // false = readwrite Preferences preferences;
preferences.begin(NVS_NAMESPACE_SCALE, false); // false = readwrite
preferences.putBool(NVS_KEY_AUTOTARE, autoTare); preferences.putBool(NVS_KEY_AUTOTARE, autoTare);
preferences.end(); preferences.end();
@@ -90,8 +87,15 @@ void start_scale(bool touchSensorConnected) {
float calibrationValue; float calibrationValue;
// NVS lesen // NVS lesen
preferences.begin(NVS_NAMESPACE, true); // true = readonly Preferences preferences;
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue); preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly
if(preferences.isKey(NVS_KEY_CALIBRATION)){
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION);
scaleCalibrated = true;
}else{
calibrationValue = SCALE_DEFAULT_CALIBRATION_VALUE;
scaleCalibrated = false;
}
// auto Tare // auto Tare
// Wenn Touch Sensor verbunden, dann autoTare auf false setzen // Wenn Touch Sensor verbunden, dann autoTare auf false setzen
@@ -106,19 +110,7 @@ void start_scale(bool touchSensorConnected) {
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN); scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
if (isnan(calibrationValue) || calibrationValue < 1) { oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale");
calibrationValue = defaultScaleCalibrationValue;
scaleCalibrated = 0;
oledShowMessage("Scale not calibrated!");
for (uint16_t i = 0; i < 50000; i++) {
yield();
vTaskDelay(pdMS_TO_TICKS(1));
esp_task_wdt_reset();
}
}
oledShowMessage("Scale Tare Please remove all");
for (uint16_t i = 0; i < 2000; i++) { for (uint16_t i = 0; i < 2000; i++) {
yield(); yield();
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
@@ -155,6 +147,8 @@ uint8_t calibrate_scale() {
uint8_t returnState = 0; uint8_t returnState = 0;
float newCalibrationValue; float newCalibrationValue;
scaleCalibrationActive = true;
vTaskSuspend(RfidReaderTask); vTaskSuspend(RfidReaderTask);
vTaskSuspend(ScaleTask); vTaskSuspend(ScaleTask);
@@ -165,7 +159,7 @@ uint8_t calibrate_scale() {
{ {
scale.set_scale(); scale.set_scale();
oledShowMessage("Step 1 empty Scale"); oledShowProgressBar(0, 3, "Scale Cal.", "Empty Scale");
for (uint16_t i = 0; i < 5000; i++) { for (uint16_t i = 0; i < 5000; i++) {
yield(); yield();
@@ -177,7 +171,7 @@ uint8_t calibrate_scale() {
Serial.println("Tare done..."); Serial.println("Tare done...");
Serial.print("Place a known weight on the scale..."); Serial.print("Place a known weight on the scale...");
oledShowMessage("Step 2 Place the weight"); oledShowProgressBar(1, 3, "Scale Cal.", "Place the weight");
for (uint16_t i = 0; i < 5000; i++) { for (uint16_t i = 0; i < 5000; i++) {
yield(); yield();
@@ -197,21 +191,20 @@ uint8_t calibrate_scale() {
Serial.println(newCalibrationValue); Serial.println(newCalibrationValue);
// Speichern mit NVS // Speichern mit NVS
preferences.begin(NVS_NAMESPACE, false); // false = readwrite Preferences preferences;
preferences.begin(NVS_NAMESPACE_SCALE, false); // false = readwrite
preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue); preferences.putFloat(NVS_KEY_CALIBRATION, newCalibrationValue);
preferences.end(); preferences.end();
// Verifizieren // Verifizieren
preferences.begin(NVS_NAMESPACE, true); preferences.begin(NVS_NAMESPACE_SCALE, true);
float verifyValue = preferences.getFloat(NVS_KEY_CALIBRATION, 0); float verifyValue = preferences.getFloat(NVS_KEY_CALIBRATION, 0);
preferences.end(); preferences.end();
Serial.print("Verified stored value: "); Serial.print("Verified stored value: ");
Serial.println(verifyValue); Serial.println(verifyValue);
Serial.println("End calibration, remove weight"); oledShowProgressBar(2, 3, "Scale Cal.", "Remove weight");
oledShowMessage("Remove weight");
scale.set_scale(newCalibrationValue); scale.set_scale(newCalibrationValue);
for (uint16_t i = 0; i < 2000; i++) { for (uint16_t i = 0; i < 2000; i++) {
@@ -220,7 +213,7 @@ uint8_t calibrate_scale() {
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
oledShowMessage("Scale calibrated"); oledShowProgressBar(3, 3, "Scale Cal.", "Completed");
// For some reason it is not possible to re-tare the scale here, it will result in a wdt timeout. Instead let the scale loop do the taring // For some reason it is not possible to re-tare the scale here, it will result in a wdt timeout. Instead let the scale loop do the taring
//scale.tare(); //scale.tare();
@@ -232,23 +225,21 @@ uint8_t calibrate_scale() {
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
scaleCalibrated = true;
returnState = 1; returnState = 1;
} }
else else
{ {
{ Serial.println("Calibration value is invalid. Please recalibrate.");
Serial.println("Calibration value is invalid. Please recalibrate.");
oledShowMessage("Calibration ERROR Try again"); oledShowProgressBar(3, 3, "Failure", "Calibration error");
for (uint16_t i = 0; i < 50000; i++) { for (uint16_t i = 0; i < 50000; i++) {
yield(); yield();
vTaskDelay(pdMS_TO_TICKS(1)); vTaskDelay(pdMS_TO_TICKS(1));
esp_task_wdt_reset(); esp_task_wdt_reset();
}
returnState = 0;
} }
returnState = 0;
} }
} }
else else
@@ -269,6 +260,7 @@ uint8_t calibrate_scale() {
vTaskResume(ScaleTask); vTaskResume(ScaleTask);
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
pauseMainTask = 0; pauseMainTask = 0;
scaleCalibrationActive = false;
return returnState; return returnState;
} }

View File

@@ -15,8 +15,9 @@ extern uint8_t weigthCouterToApi;
extern uint8_t scale_tare_counter; extern uint8_t scale_tare_counter;
extern uint8_t scaleTareRequest; extern uint8_t scaleTareRequest;
extern uint8_t pauseMainTask; extern uint8_t pauseMainTask;
extern uint8_t scaleCalibrated; extern bool scaleCalibrated;
extern bool autoTare; extern bool autoTare;
extern bool scaleCalibrationActive;
extern TaskHandle_t ScaleTask; extern TaskHandle_t ScaleTask;

View File

@@ -10,6 +10,9 @@
#include <Update.h> #include <Update.h>
#include "display.h" #include "display.h"
#include "ota.h" #include "ota.h"
#include "config.h"
#include "debug.h"
#ifndef VERSION #ifndef VERSION
#define VERSION "1.1.0" #define VERSION "1.1.0"
@@ -26,13 +29,18 @@ nfcReaderStateType lastnfcReaderState = NFC_IDLE;
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
HEAP_DEBUG_MESSAGE("onWsEvent begin");
if (type == WS_EVT_CONNECT) { if (type == WS_EVT_CONNECT) {
Serial.println("Neuer Client verbunden!"); Serial.println("Neuer Client verbunden!");
// Sende die AMS-Daten an den neuen Client // Sende die AMS-Daten an den neuen Client
if (!bambuDisabled) sendAmsData(client); if (!bambuDisabled) sendAmsData(client);
sendNfcData(client); sendNfcData();
foundNfcTag(client, 0); foundNfcTag(client, 0);
sendWriteResult(client, 3); sendWriteResult(client, 3);
// Clean up dead connections
(*server).cleanupClients();
Serial.println("Currently connected number of clients: " + String((*server).getClients().size()));
} 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_ERROR) { } else if (type == WS_EVT_ERROR) {
@@ -44,8 +52,6 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
JsonDocument doc; JsonDocument doc;
deserializeJson(doc, message); deserializeJson(doc, message);
bool spoolmanConnected = (spoolmanApiState != API_INIT);
if (doc["type"] == "heartbeat") { if (doc["type"] == "heartbeat") {
// Sende Heartbeat-Antwort // Sende Heartbeat-Antwort
ws.text(client->id(), "{" ws.text(client->id(), "{"
@@ -61,7 +67,8 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
// Versuche NFC-Daten zu schreiben // Versuche NFC-Daten zu schreiben
String payloadString; String payloadString;
serializeJson(doc["payload"], payloadString); serializeJson(doc["payload"], payloadString);
startWriteJsonToTag(payloadString.c_str());
startWriteJsonToTag((doc["tagType"] == "spool") ? true : false, payloadString.c_str());
} }
} }
@@ -113,7 +120,9 @@ void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventTyp
else { else {
Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>()); Serial.println("Unbekannter WebSocket-Typ: " + doc["type"].as<String>());
} }
doc.clear();
} }
HEAP_DEBUG_MESSAGE("onWsEvent end");
} }
// Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei // Funktion zum Laden und Ersetzen des Headers in einer HTML-Datei
@@ -140,11 +149,11 @@ void sendWriteResult(AsyncWebSocketClient *client, uint8_t success) {
void foundNfcTag(AsyncWebSocketClient *client, uint8_t success) { void foundNfcTag(AsyncWebSocketClient *client, uint8_t success) {
if (success == lastSuccess) return; if (success == lastSuccess) return;
ws.textAll("{\"type\":\"nfcTag\", \"payload\":{\"found\": " + String(success) + "}}"); ws.textAll("{\"type\":\"nfcTag\", \"payload\":{\"found\": " + String(success) + "}}");
sendNfcData(nullptr); sendNfcData();
lastSuccess = success; lastSuccess = success;
} }
void sendNfcData(AsyncWebSocketClient *client) { void sendNfcData() {
if (lastnfcReaderState == nfcReaderState) return; if (lastnfcReaderState == nfcReaderState) return;
// TBD: Why is there no status for reading the tag? // TBD: Why is there no status for reading the tag?
switch(nfcReaderState){ switch(nfcReaderState){
@@ -179,6 +188,7 @@ void sendAmsData(AsyncWebSocketClient *client) {
} }
void setupWebserver(AsyncWebServer &server) { void setupWebserver(AsyncWebServer &server) {
oledShowProgressBar(2, 7, DISPLAY_BOOT_TEXT, "Webserver init");
// Deaktiviere alle Debug-Ausgaben // Deaktiviere alle Debug-Ausgaben
Serial.setDebugOutput(false); Serial.setDebugOutput(false);
@@ -195,6 +205,9 @@ void setupWebserver(AsyncWebServer &server) {
Serial.print("Geladene Spoolman-URL: "); Serial.print("Geladene Spoolman-URL: ");
Serial.println(spoolmanUrl); Serial.println(spoolmanUrl);
// Load Bamb credentials:
loadBambuCredentials();
// Route für about // Route für about
server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("Anfrage für /about erhalten"); Serial.println("Anfrage für /about erhalten");
@@ -254,31 +267,11 @@ void setupWebserver(AsyncWebServer &server) {
html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : ""); html.replace("{{spoolmanOctoUrl}}", (octoUrl != "") ? octoUrl : "");
html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : ""); html.replace("{{spoolmanOctoToken}}", (octoToken != "") ? octoToken : "");
JsonDocument doc; html.replace("{{bambuIp}}", bambuCredentials.ip);
if (loadJsonValue("/bambu_credentials.json", doc) && doc["bambu_ip"].is<String>()) html.replace("{{bambuSerial}}", bambuCredentials.serial);
{ html.replace("{{bambuCode}}", bambuCredentials.accesscode ? bambuCredentials.accesscode : "");
String bambuIp = doc["bambu_ip"].as<String>(); html.replace("{{autoSendToBambu}}", bambuCredentials.autosend_enable ? "checked" : "");
String bambuSerial = doc["bambu_serialnr"].as<String>(); html.replace("{{autoSendTime}}", (bambuCredentials.autosend_time != 0) ? String(bambuCredentials.autosend_time) : String(BAMBU_DEFAULT_AUTOSEND_TIME));
String bambuCode = doc["bambu_accesscode"].as<String>();
autoSendToBambu = doc["autoSendToBambu"].as<bool>();
bambuIp.trim();
bambuSerial.trim();
bambuCode.trim();
html.replace("{{bambuIp}}", bambuIp ? bambuIp : "");
html.replace("{{bambuSerial}}", bambuSerial ? bambuSerial : "");
html.replace("{{bambuCode}}", bambuCode ? bambuCode : "");
html.replace("{{autoSendToBambu}}", autoSendToBambu ? "checked" : "");
html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
}
else
{
html.replace("{{bambuIp}}", "");
html.replace("{{bambuSerial}}", "");
html.replace("{{bambuCode}}", "");
html.replace("{{autoSendToBambu}}", "");
html.replace("{{autoSendTime}}", String(autoSetBambuAmsCounter));
}
request->send(200, "text/html", html); request->send(200, "text/html", html);
}); });

View File

@@ -24,7 +24,7 @@ void setupWebserver(AsyncWebServer &server);
// WebSocket-Funktionen // WebSocket-Funktionen
void sendAmsData(AsyncWebSocketClient *client); void sendAmsData(AsyncWebSocketClient *client);
void sendNfcData(AsyncWebSocketClient *client); void sendNfcData();
void foundNfcTag(AsyncWebSocketClient *client, uint8_t success); void foundNfcTag(AsyncWebSocketClient *client, uint8_t success);
void sendWriteResult(AsyncWebSocketClient *client, uint8_t success); void sendWriteResult(AsyncWebSocketClient *client, uint8_t success);

View File

@@ -61,8 +61,7 @@ void initWiFi() {
wm.setWiFiAutoReconnect(true); wm.setWiFiAutoReconnect(true);
wm.setConnectTimeout(10); wm.setConnectTimeout(10);
oledShowTopRow(); oledShowProgressBar(1, 7, DISPLAY_BOOT_TEXT, "WiFi init");
oledShowMessage("WiFi Setup");
//bool res = wm.autoConnect("FilaMan"); // anonymous ap //bool res = wm.autoConnect("FilaMan"); // anonymous ap
if(!wm.autoConnect("FilaMan")) { if(!wm.autoConnect("FilaMan")) {
@@ -80,9 +79,6 @@ void initWiFi() {
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
oledShowTopRow(); oledShowTopRow();
display.display();
vTaskDelay(500 / portTICK_PERIOD_MS);
// mDNS // mDNS
startMDNS(); startMDNS();