Merge pull request #45 from janecker/nfc_write_improvements

Nfc write improvements
This commit is contained in:
2025-08-10 14:38:11 +02:00
committed by GitHub
9 changed files with 428 additions and 377 deletions

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,78 +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',
tagType: 'spool', tagType: 'spool',
payload: nfcData payload: nfcData
})); }));
} else { } else {
alert('Not connected to Server. Please check connection.'); 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({
tagType: 'location', type: 'writeNfcTag',
payload: nfcData tagType: 'location',
})); payload: nfcData
} else { }));
alert('Not connected to Server. Please check connection.'); } else {
alert('Not connected to Server. Please check connection.');
}
} }
} }

View File

@@ -5,7 +5,7 @@
#include <Preferences.h> #include <Preferences.h>
#include "debug.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;
@@ -14,6 +14,8 @@ String octoUrl = "";
String octoToken = ""; String octoToken = "";
uint16_t remainingWeight = 0; uint16_t remainingWeight = 0;
bool spoolmanConnected = false; bool spoolmanConnected = false;
bool spoolmanExtraFieldsChecked = false;
TaskHandle_t* apiTask;
struct SendToApiParams { struct SendToApiParams {
SpoolmanApiRequestType requestType; SpoolmanApiRequestType requestType;
@@ -94,6 +96,11 @@ JsonDocument fetchSingleSpoolInfo(int spoolId) {
void sendToApi(void *parameter) { void sendToApi(void *parameter) {
HEAP_DEBUG_MESSAGE("sendToApi begin"); 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;
@@ -236,7 +243,7 @@ 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();
@@ -282,7 +289,7 @@ 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();
@@ -319,17 +326,17 @@ uint8_t updateSpoolLocation(String spoolId, String location){
params->spoolsUrl = spoolsUrl; params->spoolsUrl = spoolsUrl;
params->updatePayload = updatePayload; params->updatePayload = updatePayload;
if(spoolmanApiState == API_IDLE){
// Erstelle die Task
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
NULL // Task-Handle (nicht benötigt)
);
if(apiTask == nullptr){
// Erstelle die Task
BaseType_t result = xTaskCreate(
sendToApi, // Task-Funktion
"SendToApiTask", // Task-Name
6144, // Stackgröße in Bytes
(void*)params, // Parameter
0, // Priorität
apiTask // Task-Handle
);
}else{ }else{
Serial.println("Not spawning new task, API still active!"); Serial.println("Not spawning new task, API still active!");
} }
@@ -374,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();
@@ -427,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;
@@ -435,198 +442,222 @@ 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\","
"\"unit\": \"\","
"\"field_type\": \"float\","
"\"key\": \"price_gramm\"}",
"{\"name\": \"Bambu Setting ID\","
"\"field_type\": \"text\","
"\"key\": \"bambu_setting_id\"}",
"{\"name\": \"Bambu Cali ID\","
"\"field_type\": \"text\","
"\"key\": \"bambu_cali_id\"}",
"{\"name\": \"Bambu Filament IDX\","
"\"field_type\": \"text\","
"\"key\": \"bambu_idx\"}",
"{\"name\": \"Bambu k\","
"\"field_type\": \"float\","
"\"key\": \"bambu_k\"}",
"{\"name\": \"Bambu Flow Ratio\","
"\"field_type\": \"float\","
"\"key\": \"bambu_flow_ratio\"}",
"{\"name\": \"Bambu Max Vol. Speed\","
"\"unit\": \"mm3/s\","
"\"field_type\": \"integer\","
"\"default_value\": \"12\","
"\"key\": \"bambu_max_volspeed\"}"
};
Serial.println("Überprüfe Extrafelder...");
int urlLength = sizeof(checkUrls) / sizeof(checkUrls[0]);
for (uint8_t i = 0; i < urlLength; i++) {
Serial.println();
Serial.println("-------- Prüfe Felder für "+checkUrls[i]+" --------");
http.begin(checkUrls[i]);
int httpCode = http.GET();
"{\"name\": \"Price/g\"," if (httpCode == HTTP_CODE_OK) {
"\"unit\": \"\"," String payload = http.getString();
"\"field_type\": \"float\"," JsonDocument doc;
"\"key\": \"price_gramm\"}", DeserializationError error = deserializeJson(doc, payload);
if (!error) {
String* extraFields;
String* extraFieldData;
u16_t extraLength;
"{\"name\": \"Bambu Setting ID\"," if (i == 0) {
"\"field_type\": \"text\"," extraFields = spoolExtra;
"\"key\": \"bambu_setting_id\"}", extraFieldData = spoolExtraFields;
extraLength = sizeof(spoolExtra) / sizeof(spoolExtra[0]);
"{\"name\": \"Bambu Cali ID\"," } else {
"\"field_type\": \"text\"," extraFields = filamentExtra;
"\"key\": \"bambu_cali_id\"}", extraFieldData = filamentExtraFields;
extraLength = sizeof(filamentExtra) / sizeof(filamentExtra[0]);
"{\"name\": \"Bambu Filament IDX\","
"\"field_type\": \"text\","
"\"key\": \"bambu_idx\"}",
"{\"name\": \"Bambu k\","
"\"field_type\": \"float\","
"\"key\": \"bambu_k\"}",
"{\"name\": \"Bambu Flow Ratio\","
"\"field_type\": \"float\","
"\"key\": \"bambu_flow_ratio\"}",
"{\"name\": \"Bambu Max Vol. Speed\","
"\"unit\": \"mm3/s\","
"\"field_type\": \"integer\","
"\"default_value\": \"12\","
"\"key\": \"bambu_max_volspeed\"}"
};
Serial.println("Überprüfe Extrafelder...");
int urlLength = sizeof(checkUrls) / sizeof(checkUrls[0]);
for (uint8_t i = 0; i < urlLength; i++) {
Serial.println();
Serial.println("-------- Prüfe Felder für "+checkUrls[i]+" --------");
http.begin(checkUrls[i]);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
String* extraFields;
String* extraFieldData;
u16_t extraLength;
if (i == 0) {
extraFields = spoolExtra;
extraFieldData = spoolExtraFields;
extraLength = sizeof(spoolExtra) / sizeof(spoolExtra[0]);
} else {
extraFields = filamentExtra;
extraFieldData = filamentExtraFields;
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();
} }
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();
String payload = http.getString();
JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload);
if (!error && doc["status"].is<String>()) {
const char* status = doc["status"];
http.end();
if (!checkSpoolmanExtraFields()) { if (httpCode > 0) {
Serial.println("Fehler beim Überprüfen der Extrafelder."); if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload);
if (!error && doc["status"].is<String>()) {
const char* status = doc["status"];
http.end();
// TBD if (!checkSpoolmanExtraFields()) {
oledShowMessage("Spoolman Error creating Extrafields"); Serial.println("Fehler beim Überprüfen der Extrafelder.");
vTaskDelay(2000 / portTICK_PERIOD_MS);
// TBD
return false; oledShowMessage("Spoolman Error creating Extrafields");
vTaskDelay(2000 / portTICK_PERIOD_MS);
return false;
}
spoolmanApiState = API_IDLE;
oledShowTopRow();
spoolmanConnected = true;
returnValue = strcmp(status, "healthy") == 0;
}else{
spoolmanConnected = false;
} }
spoolmanApiState = API_IDLE; doc.clear();
oledShowTopRow(); }else{
spoolmanConnected = true; spoolmanConnected = false;
return strcmp(status, "healthy") == 0;
} }
} else {
doc.clear(); 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& octo_url, const String& octoTk) { bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, const String& octoTk) {
@@ -639,12 +670,13 @@ bool saveSpoolmanUrl(const String& url, bool octoOn, const String& octo_url, con
preferences.end(); preferences.end();
//TBD: This could be handled nicer in the future //TBD: This could be handled nicer in the future
spoolmanExtraFieldsChecked = false;
spoolmanUrl = url; spoolmanUrl = url;
octoEnabled = octoOn; octoEnabled = octoOn;
octoUrl = octo_url; octoUrl = octo_url;
octoToken = octoTk; octoToken = octoTk;
return true; return checkSpoolmanInstance();
} }
String loadSpoolmanUrl() { String loadSpoolmanUrl() {
@@ -664,15 +696,10 @@ String loadSpoolmanUrl() {
bool initSpoolman() { bool initSpoolman() {
oledShowProgressBar(3, 7, DISPLAY_BOOT_TEXT, "Spoolman init"); oledShowProgressBar(3, 7, DISPLAY_BOOT_TEXT, "Spoolman init");
spoolmanUrl = loadSpoolmanUrl(); spoolmanUrl = loadSpoolmanUrl();
spoolmanUrl.trim();
if (spoolmanUrl == "") { bool success = checkSpoolmanInstance();
Serial.println("Keine Spoolman-URL gefunden.");
return false;
}
bool success = checkSpoolmanInstance(spoolmanUrl);
if (!success) { if (!success) {
Serial.println("Spoolman nicht erreichbar."); Serial.println("Spoolman not available");
return false; return false;
} }

View File

@@ -29,7 +29,7 @@ extern String octoUrl;
extern String octoToken; extern String octoToken;
extern bool spoolmanConnected; 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

@@ -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)

View File

@@ -3,36 +3,39 @@
#include <Arduino.h> #include <Arduino.h>
#define BAMBU_DEFAULT_AUTOSEND_TIME 60 #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_API "api" #define NVS_NAMESPACE_BAMBU "bambu"
#define NVS_KEY_SPOOLMAN_URL "spoolmanUrl" #define NVS_KEY_BAMBU_IP "bambuIp"
#define NVS_KEY_OCTOPRINT_ENABLED "octoEnabled" #define NVS_KEY_BAMBU_ACCESSCODE "bambuCode"
#define NVS_KEY_OCTOPRINT_URL "octoUrl" #define NVS_KEY_BAMBU_SERIAL "bambuSerial"
#define NVS_KEY_OCTOPRINT_TOKEN "octoToken" #define NVS_KEY_BAMBU_AUTOSEND_ENABLE "autosendEnable"
#define NVS_KEY_BAMBU_AUTOSEND_TIME "autosendTime"
#define NVS_NAMESPACE_BAMBU "bambu" #define NVS_NAMESPACE_SCALE "scale"
#define NVS_KEY_BAMBU_IP "bambuIp" #define NVS_KEY_CALIBRATION "cal_value"
#define NVS_KEY_BAMBU_ACCESSCODE "bambuCode" #define NVS_KEY_AUTOTARE "auto_tare"
#define NVS_KEY_BAMBU_SERIAL "bambuSerial" #define SCALE_DEFAULT_CALIBRATION_VALUE 430.0f;
#define NVS_KEY_BAMBU_AUTOSEND_ENABLE "autosendEnable"
#define NVS_KEY_BAMBU_AUTOSEND_TIME "autosendTime"
#define NVS_NAMESPACE_SCALE "scale" #define BAMBU_USERNAME "bblp"
#define NVS_KEY_CALIBRATION "cal_value"
#define NVS_KEY_AUTOTARE "auto_tare"
#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 OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) #define SCREEN_WIDTH 128U
#define SCREEN_ADDRESS 0x3CU // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 #define SCREEN_HEIGHT 64U
#define SCREEN_WIDTH 128U #define SCREEN_TOP_BAR_HEIGHT 16U
#define SCREEN_HEIGHT 64U #define SCREEN_PROGRESS_BAR_HEIGHT 12U
#define SCREEN_TOP_BAR_HEIGHT 16U #define DISPLAY_BOOT_TEXT "FilaMan"
#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;

View File

@@ -97,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;
@@ -115,17 +116,23 @@ 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 // Periodic display update
if (intervalElapsed(currentMillis, lastWifiCheckTime, 1000)) if (intervalElapsed(currentMillis, lastTopRowUpdateTime, DISPLAY_UPDATE_INTERVAL))
{ {
oledShowTopRow(); 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 (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0) if (bambuCredentials.autosend_enable && autoSetToBambuSpoolId > 0)
{ {
@@ -156,94 +163,93 @@ 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 && (!bambuCredentials.autosend_enable || 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;
}
else // 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;
}
}
// 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;
} }
}
lastWeight = weight;
// reset weight counter after writing tag // Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden
// TBD: what exactly is the logic behind this? if (activeSpoolId != "" && weigthCouterToApi > 3 && weightSend == 0 && nfcReaderState == NFC_READ_SUCCESS && tagProcessed == false && spoolmanApiState == API_IDLE) {
if (currentMillis - lastWeightReadTime >= weightReadInterval && nfcReaderState != NFC_IDLE && nfcReaderState != NFC_READ_SUCCESS) // set the current tag as processed to prevent it beeing processed again
{ tagProcessed = true;
weigthCouterToApi = 0;
}
lastWeight = weight;
// Wenn ein Tag mit SM id erkannte wurde und der Waage Counter anspricht an SM Senden if (updateSpoolWeight(activeSpoolId, weight))
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 weightSend = 1;
tagProcessed = true;
}
if (updateSpoolWeight(activeSpoolId, weight)) else
{ {
weightSend = 1; oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
} }
else
{
oledShowIcon("failed");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
if(sendOctoUpdate && spoolmanApiState == API_IDLE){ if(sendOctoUpdate && spoolmanApiState == API_IDLE){
autoSetToBambuSpoolId = activeSpoolId.toInt(); autoSetToBambuSpoolId = activeSpoolId.toInt();
if(octoEnabled) if(octoEnabled)
{ {
updateSpoolOcto(autoSetToBambuSpoolId); updateSpoolOcto(autoSetToBambuSpoolId);
}
sendOctoUpdate = false;
} }
sendOctoUpdate = false;
} }
esp_task_wdt_reset(); esp_task_wdt_reset();

View File

@@ -8,6 +8,7 @@
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "scale.h" #include "scale.h"
#include "bambu.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);
@@ -20,6 +21,8 @@ String lastSpoolId = "";
String nfcJsonData = ""; String nfcJsonData = "";
bool tagProcessed = false; bool tagProcessed = false;
volatile bool pauseBambuMqttTask = false; volatile bool pauseBambuMqttTask = false;
volatile bool nfcReadingTaskSuspendRequest = false;
volatile bool nfcReadingTaskSuspendState = false;
struct NfcWriteParameterType { struct NfcWriteParameterType {
bool tagType; bool tagType;
@@ -238,8 +241,6 @@ bool decodeNdefAndReturnJson(const byte* encodedMessage) {
Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>()); Serial.println("SPOOL-ID gefunden: " + doc["sm_id"].as<String>());
activeSpoolId = doc["sm_id"].as<String>(); activeSpoolId = doc["sm_id"].as<String>();
lastSpoolId = activeSpoolId; lastSpoolId = activeSpoolId;
Serial.println("Api state: " + String(spoolmanApiState));
} }
else if(doc["location"].is<String>() && doc["location"] != "") else if(doc["location"].is<String>() && doc["location"] != "")
{ {
@@ -278,19 +279,21 @@ void writeJsonToTag(void *parameter) {
Serial.println(params->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(); sendNfcData();
vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelay(100 / portTICK_PERIOD_MS);
Serial.println("CP 1");
// 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++) {
Serial.println("CP 2");
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;
// yield before potentially waiting for 400ms // yield before potentially waiting for 400ms
@@ -298,7 +301,6 @@ void writeJsonToTag(void *parameter) {
esp_task_wdt_reset(); esp_task_wdt_reset();
success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400); success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 400);
if (success) { if (success) {
Serial.println("CP 3.1");
for (uint8_t i = 0; i < uidLength; i++) { for (uint8_t i = 0; i < uidLength; i++) {
//TBD: Rework to remove all the string operations //TBD: Rework to remove all the string operations
uidString += String(uid[i], HEX); uidString += String(uid[i], HEX);
@@ -308,8 +310,6 @@ void writeJsonToTag(void *parameter) {
} }
foundNfcTag(nullptr, success); foundNfcTag(nullptr, success);
break; break;
}else{
Serial.println("CP 3.2");
} }
yield(); yield();
@@ -372,7 +372,7 @@ void writeJsonToTag(void *parameter) {
sendWriteResult(nullptr, success); sendWriteResult(nullptr, success);
sendNfcData(); sendNfcData();
vTaskResume(RfidReaderTask); nfcReadingTaskSuspendRequest = false;
pauseBambuMqttTask = false; pauseBambuMqttTask = false;
vTaskDelete(NULL); vTaskDelete(NULL);
@@ -384,7 +384,7 @@ void startWriteJsonToTag(const bool isSpoolTag, const char* payload) {
parameters->payload = strdup(payload); parameters->payload = strdup(payload);
// Task nicht mehrfach starten // Task nicht mehrfach starten
if (nfcReaderState == NFC_IDLE) { if (nfcReaderState == NFC_IDLE || nfcReaderState == NFC_READ_ERROR || nfcReaderState == NFC_READ_SUCCESS) {
oledShowProgressBar(0, 1, "Write Tag", "Place tag now"); oledShowProgressBar(0, 1, "Write Tag", "Place tag now");
// Erstelle die Task // Erstelle die Task
xTaskCreate( xTaskCreate(
@@ -405,15 +405,16 @@ 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);
@@ -430,8 +431,8 @@ void scanRfidTask(void * parameter) {
oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag"); oledShowProgressBar(0, octoEnabled?5:4, "Reading", "Detecting tag");
vTaskDelay(500 / portTICK_PERIOD_MS); //vTaskDelay(500 / portTICK_PERIOD_MS);
if (uidLength == 7) if (uidLength == 7)
{ {
uint16_t tagSize = readTagSize(); uint16_t tagSize = readTagSize();
@@ -487,7 +488,7 @@ void scanRfidTask(void * parameter) {
} }
} }
if (!success && nfcReaderState != NFC_IDLE) if (!success && nfcReaderState != NFC_IDLE && !nfcReadingTaskSuspendRequest)
{ {
nfcReaderState = NFC_IDLE; nfcReaderState = NFC_IDLE;
//uidString = ""; //uidString = "";
@@ -500,6 +501,12 @@ void scanRfidTask(void * parameter) {
// aktualisieren der Website wenn sich der Status ändert // aktualisieren der Website wenn sich der Status ändert
sendNfcData(); sendNfcData();
} }
else
{
nfcReadingTaskSuspendState = true;
Serial.println("NFC Reading disabled");
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
yield(); yield();
} }
} }

View File

@@ -17,8 +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;
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) {
@@ -88,7 +89,13 @@ void start_scale(bool touchSensorConnected) {
// NVS lesen // NVS lesen
Preferences preferences; Preferences preferences;
preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly preferences.begin(NVS_NAMESPACE_SCALE, true); // true = readonly
calibrationValue = preferences.getFloat(NVS_KEY_CALIBRATION, defaultScaleCalibrationValue); 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
@@ -103,18 +110,6 @@ 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) {
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();
}
}
oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale"); oledShowProgressBar(6, 7, DISPLAY_BOOT_TEXT, "Tare scale");
for (uint16_t i = 0; i < 2000; i++) { for (uint16_t i = 0; i < 2000; i++) {
yield(); yield();
@@ -152,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);
@@ -228,6 +225,7 @@ uint8_t calibrate_scale() {
esp_task_wdt_reset(); esp_task_wdt_reset();
} }
scaleCalibrated = true;
returnState = 1; returnState = 1;
} }
else else
@@ -262,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;