1 Commits

Author SHA1 Message Date
3a564e713a Release 2026-04-14 spool and filament workflow 2026-04-14 16:39:02 +03:00
4 changed files with 459 additions and 17 deletions

View File

@@ -30,6 +30,7 @@ constexpr uint32_t kWifiConnectTimeoutMs = 20000;
constexpr uint32_t kSensorTimeoutMs = 1500; constexpr uint32_t kSensorTimeoutMs = 1500;
constexpr uint8_t kAverageSamples = 16; constexpr uint8_t kAverageSamples = 16;
constexpr uint8_t kRawFilterWindow = 8; constexpr uint8_t kRawFilterWindow = 8;
constexpr uint8_t kBatchCodeMaxLength = 4;
constexpr float kRawFilterAlpha = 0.20f; constexpr float kRawFilterAlpha = 0.20f;
constexpr float kWeightStepHysteresisGrams = 0.75f; constexpr float kWeightStepHysteresisGrams = 0.75f;
constexpr lv_coord_t kScreenMargin = 12; constexpr lv_coord_t kScreenMargin = 12;
@@ -71,10 +72,12 @@ struct WifiData
struct InventreeMatch struct InventreeMatch
{ {
int id = 0; int id = 0;
int part_id = 0;
float quantity = 0.0f; float quantity = 0.0f;
String location; String location;
String part_name; String part_name;
String batch; String batch;
String spool_weight;
}; };
constexpr size_t kInventreeMaxMatches = 8; constexpr size_t kInventreeMaxMatches = 8;
@@ -109,6 +112,8 @@ struct UiRefs
lv_obj_t *weight_note_label = nullptr; lv_obj_t *weight_note_label = nullptr;
lv_obj_t *weight_raw_value = nullptr; lv_obj_t *weight_raw_value = nullptr;
lv_obj_t *workflow_batch_ta = nullptr; lv_obj_t *workflow_batch_ta = nullptr;
lv_obj_t *workflow_spool_weight_value = nullptr;
lv_obj_t *workflow_filament_weight_value = nullptr;
lv_obj_t *workflow_stock_name_label = nullptr; lv_obj_t *workflow_stock_name_label = nullptr;
lv_obj_t *workflow_stock_meta_label = nullptr; lv_obj_t *workflow_stock_meta_label = nullptr;
@@ -173,6 +178,7 @@ void saveInventreeConfig();
void loadInventreeConfig(); void loadInventreeConfig();
float getCurrentWeightGrams(); float getCurrentWeightGrams();
bool getFilamentWeightGrams(int32_t &grams);
void performTare(); void performTare();
void performCalibration(float reference_grams); void performCalibration(float reference_grams);
void clearCalibration(); void clearCalibration();

View File

@@ -4,6 +4,7 @@
#include <HTTPClient.h> #include <HTTPClient.h>
#include <WiFiClient.h> #include <WiFiClient.h>
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include <ctype.h>
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -11,6 +12,13 @@
namespace namespace
{ {
bool performInventreeRequest(const char *method,
const String &path,
const String &request_body,
int &http_code,
String &response_body,
String &error_text);
String normalizeInventreeBaseUrl(const String &value) String normalizeInventreeBaseUrl(const String &value)
{ {
String base_url = value; String base_url = value;
@@ -50,6 +58,165 @@ String urlEncode(const String &value)
return encoded; return encoded;
} }
void sanitizeBatchCode(String &value)
{
value.trim();
if (value.length() > kBatchCodeMaxLength)
{
value.remove(kBatchCodeMaxLength);
}
}
String formatSpoolWeightValue(const String &value_text)
{
String value = value_text;
value.trim();
return value;
}
String normalizeParameterKey(const String &value)
{
String normalized;
normalized.reserve(value.length());
for (size_t i = 0; i < value.length(); ++i)
{
const unsigned char ch = static_cast<unsigned char>(value[i]);
if (isalnum(ch))
{
normalized += static_cast<char>(tolower(ch));
}
}
return normalized;
}
bool isSpoolWeightParameterName(const String &parameter_name)
{
const String normalized_name = normalizeParameterKey(parameter_name);
return normalized_name == "spoolwegh" || normalized_name == "spoolweght" || normalized_name == "spoolweight";
}
String extractSpoolWeightFromPartDetail(JsonVariantConst part_detail)
{
JsonVariantConst parameters_map = part_detail["parameters_map"];
if (parameters_map.isNull())
{
return "";
}
const char *const spool_weight =
parameters_map["SpoolWegh"] |
parameters_map["SpoolWeght"] |
parameters_map["SpoolWeight"] |
parameters_map["spool_weight"] |
parameters_map["spoolwegh"] |
parameters_map["spoolweght"];
return spool_weight != nullptr ? formatSpoolWeightValue(String(spool_weight)) : String();
}
String formatInventreeParameterValue(JsonObjectConst parameter)
{
String value = String(static_cast<const char *>(parameter["data"] | ""));
value.trim();
if (value.isEmpty())
{
const JsonVariantConst numeric_value = parameter["data_numeric"];
if (!numeric_value.isNull())
{
const float number = numeric_value.as<float>();
const bool whole_number = fabsf(number - roundf(number)) < 0.05f;
value = String(number, whole_number ? 0 : 1);
}
}
String units = String(static_cast<const char *>(parameter["template_detail"]["units"] | ""));
units.trim();
if (!value.isEmpty() && !units.isEmpty())
{
String normalized_value = value;
normalized_value.toLowerCase();
String normalized_units = units;
normalized_units.toLowerCase();
if (!normalized_value.endsWith(normalized_units))
{
value += " ";
value += units;
}
}
return value;
}
String extractSpoolWeightFromParameters(JsonArrayConst parameters)
{
for (JsonObjectConst parameter : parameters)
{
const String parameter_name = String(static_cast<const char *>(parameter["template_detail"]["name"] | ""));
if (isSpoolWeightParameterName(parameter_name))
{
return formatInventreeParameterValue(parameter);
}
}
return "";
}
bool tryParseSpoolWeightGrams(int32_t &grams)
{
grams = 0;
if (inventree_data.selected_match < 0 || inventree_data.selected_match >= inventree_data.stored_matches)
{
return false;
}
const String &spool_weight = inventree_data.matches[inventree_data.selected_match].spool_weight;
if (spool_weight.isEmpty())
{
return false;
}
bool has_digit = false;
for (size_t i = 0; i < spool_weight.length(); ++i)
{
if (isdigit(static_cast<unsigned char>(spool_weight[i])))
{
has_digit = true;
break;
}
}
if (!has_digit)
{
return false;
}
const float parsed_value = spool_weight.toFloat();
grams = parsed_value <= 0.0f ? 0 : static_cast<int32_t>(lroundf(parsed_value));
return true;
}
bool tryGetMeasuredWeightGrams(int32_t &grams)
{
grams = 0;
if (!sensor_online || !calibration.has_offset || calibration.scale <= 0.0001f)
{
return false;
}
const int32_t stable_grams = displayed_weight_valid ? displayed_weight_grams : static_cast<int32_t>(lroundf(getCurrentWeightGrams()));
grams = stable_grams < 0 ? 0 : stable_grams;
return true;
}
void syncInventreeInputs(bool persist) void syncInventreeInputs(bool persist)
{ {
inventree_data.base_url = ui.inventree_url_ta != nullptr inventree_data.base_url = ui.inventree_url_ta != nullptr
@@ -77,7 +244,16 @@ void syncInventreeInputs(bool persist)
inventree_data.batch = batch_input != nullptr inventree_data.batch = batch_input != nullptr
? String(lv_textarea_get_text(batch_input)) ? String(lv_textarea_get_text(batch_input))
: inventree_data.batch; : inventree_data.batch;
inventree_data.batch.trim(); sanitizeBatchCode(inventree_data.batch);
if (batch_input != nullptr)
{
const String current_batch_text = String(lv_textarea_get_text(batch_input));
if (current_batch_text != inventree_data.batch)
{
lv_textarea_set_text(batch_input, inventree_data.batch.c_str());
}
}
if (ui.workflow_batch_ta != nullptr && batch_input != ui.workflow_batch_ta) if (ui.workflow_batch_ta != nullptr && batch_input != ui.workflow_batch_ta)
{ {
@@ -144,6 +320,12 @@ void updateInventreeSelectionSummary()
} }
} }
if (!match.spool_weight.isEmpty())
{
text += "\nSpool ";
text += match.spool_weight;
}
inventree_data.result_text = text; inventree_data.result_text = text;
updateInventreeLabels(); updateInventreeLabels();
} }
@@ -229,6 +411,61 @@ bool performInventreeHttpRequest(TClient &client,
return http_code > 0; return http_code > 0;
} }
bool loadSpoolWeightForMatch(InventreeMatch &match, String &error_text)
{
error_text = "";
if (!match.spool_weight.isEmpty() || match.part_id <= 0)
{
return true;
}
int http_code = 0;
String response_body;
const String path = "/api/part/" + String(match.part_id) + "/?parameters=true";
if (!performInventreeRequest("GET", path, "", http_code, response_body, error_text))
{
return false;
}
if (http_code != 200)
{
error_text = response_body.length() > 0 ? response_body : "Unexpected part response.";
return false;
}
JsonDocument filter;
filter["parameters_map"]["SpoolWegh"] = true;
filter["parameters_map"]["SpoolWeght"] = true;
filter["parameters_map"]["SpoolWeight"] = true;
filter["parameters_map"]["spool_weight"] = true;
filter["parameters_map"]["spoolwegh"] = true;
filter["parameters_map"]["spoolweght"] = true;
JsonArray parameters_filter = filter["parameters"].to<JsonArray>();
JsonObject parameter_filter = parameters_filter.add<JsonObject>();
parameter_filter["data"] = true;
parameter_filter["data_numeric"] = true;
parameter_filter["template_detail"]["name"] = true;
parameter_filter["template_detail"]["units"] = true;
JsonDocument doc;
const DeserializationError json_error = deserializeJson(doc, response_body, DeserializationOption::Filter(filter));
if (json_error)
{
error_text = json_error.c_str();
return false;
}
match.spool_weight = extractSpoolWeightFromPartDetail(doc.as<JsonVariantConst>());
if (match.spool_weight.isEmpty())
{
match.spool_weight = extractSpoolWeightFromParameters(doc["parameters"].as<JsonArrayConst>());
}
return true;
}
bool requestInventreeToken(String &error_text) bool requestInventreeToken(String &error_text)
{ {
error_text = ""; error_text = "";
@@ -378,18 +615,43 @@ bool getMeasuredWeightForInventree(String &quantity_text)
{ {
quantity_text = ""; quantity_text = "";
if (!sensor_online || !calibration.has_offset || calibration.scale <= 0.0001f) int32_t filament_grams = 0;
if (!getFilamentWeightGrams(filament_grams))
{ {
return false; return false;
} }
const int32_t stable_grams = displayed_weight_valid ? displayed_weight_grams : static_cast<int32_t>(lroundf(getCurrentWeightGrams())); quantity_text = String(filament_grams);
quantity_text = String(stable_grams < 0 ? 0 : stable_grams);
return true; return true;
} }
} // namespace } // namespace
bool getFilamentWeightGrams(int32_t &grams)
{
grams = 0;
int32_t measured_grams = 0;
if (!tryGetMeasuredWeightGrams(measured_grams))
{
return false;
}
int32_t spool_grams = 0;
if (!tryParseSpoolWeightGrams(spool_grams))
{
return false;
}
grams = measured_grams - spool_grams;
if (grams < 0)
{
grams = 0;
}
return true;
}
void saveWifiConfig() void saveWifiConfig()
{ {
prefs.putString("wifi_ssid", wifi_data.ssid); prefs.putString("wifi_ssid", wifi_data.ssid);
@@ -424,7 +686,21 @@ void loadInventreeConfig()
} }
inventree_data.token = prefs.getString("inv_tok", ""); inventree_data.token = prefs.getString("inv_tok", "");
inventree_data.batch = prefs.getString("inv_batch", prefs.getString("inv_ipn", ""));
if (prefs.isKey("inv_batch"))
{
inventree_data.batch = prefs.getString("inv_batch", "");
}
else if (prefs.isKey("inv_ipn"))
{
inventree_data.batch = prefs.getString("inv_ipn", "");
}
else
{
inventree_data.batch = "";
}
sanitizeBatchCode(inventree_data.batch);
} }
bool inventreeConfigured() bool inventreeConfigured()
@@ -437,6 +713,11 @@ void clearInventreeMatches()
inventree_data.total_matches = 0; inventree_data.total_matches = 0;
inventree_data.stored_matches = 0; inventree_data.stored_matches = 0;
inventree_data.selected_match = -1; inventree_data.selected_match = -1;
for (size_t i = 0; i < kInventreeMaxMatches; ++i)
{
inventree_data.matches[i] = InventreeMatch();
}
} }
void connectWifi(const String &ssid, const String &password, bool save_credentials) void connectWifi(const String &ssid, const String &password, bool save_credentials)
@@ -650,12 +931,19 @@ void findInventreeStockByBatch()
JsonArray filter_results = filter["results"].to<JsonArray>(); JsonArray filter_results = filter["results"].to<JsonArray>();
JsonObject filter_item = filter_results.add<JsonObject>(); JsonObject filter_item = filter_results.add<JsonObject>();
filter_item["pk"] = true; filter_item["pk"] = true;
filter_item["part"] = true;
filter_item["batch"] = true; filter_item["batch"] = true;
filter_item["quantity"] = true; filter_item["quantity"] = true;
filter_item["location"] = true; filter_item["location"] = true;
filter_item["location_detail"]["pathstring"] = true; filter_item["location_detail"]["pathstring"] = true;
filter_item["location_detail"]["name"] = true; filter_item["location_detail"]["name"] = true;
filter_item["part_detail"]["name"] = true; filter_item["part_detail"]["name"] = true;
filter_item["part_detail"]["parameters_map"]["SpoolWegh"] = true;
filter_item["part_detail"]["parameters_map"]["SpoolWeght"] = true;
filter_item["part_detail"]["parameters_map"]["SpoolWeight"] = true;
filter_item["part_detail"]["parameters_map"]["spool_weight"] = true;
filter_item["part_detail"]["parameters_map"]["spoolwegh"] = true;
filter_item["part_detail"]["parameters_map"]["spoolweght"] = true;
JsonDocument doc; JsonDocument doc;
const DeserializationError json_error = deserializeJson(doc, response, DeserializationOption::Filter(filter)); const DeserializationError json_error = deserializeJson(doc, response, DeserializationOption::Filter(filter));
@@ -680,10 +968,13 @@ void findInventreeStockByBatch()
} }
InventreeMatch &match = inventree_data.matches[inventree_data.stored_matches]; InventreeMatch &match = inventree_data.matches[inventree_data.stored_matches];
match = InventreeMatch();
match.id = item["pk"] | 0; match.id = item["pk"] | 0;
match.part_id = item["part"] | 0;
match.quantity = item["quantity"] | 0.0f; match.quantity = item["quantity"] | 0.0f;
match.batch = String(static_cast<const char *>(item["batch"] | "")); match.batch = String(static_cast<const char *>(item["batch"] | ""));
match.part_name = String(static_cast<const char *>(item["part_detail"]["name"] | "")); match.part_name = String(static_cast<const char *>(item["part_detail"]["name"] | ""));
match.spool_weight = extractSpoolWeightFromPartDetail(item["part_detail"]);
const char *pathstring = item["location_detail"]["pathstring"] | nullptr; const char *pathstring = item["location_detail"]["pathstring"] | nullptr;
const char *name = item["location_detail"]["name"] | nullptr; const char *name = item["location_detail"]["name"] | nullptr;
@@ -713,6 +1004,22 @@ void findInventreeStockByBatch()
} }
inventree_data.selected_match = 0; inventree_data.selected_match = 0;
if (inventree_data.selected_match < inventree_data.stored_matches)
{
String spool_error;
if (!loadSpoolWeightForMatch(inventree_data.matches[inventree_data.selected_match], spool_error))
{
Serial.printf("InvenTree spool weight fallback failed for part %d: %s\n",
inventree_data.matches[inventree_data.selected_match].part_id,
spool_error.c_str());
}
else
{
Serial.printf("InvenTree spool weight for part %d: %s\n",
inventree_data.matches[inventree_data.selected_match].part_id,
inventree_data.matches[inventree_data.selected_match].spool_weight.c_str());
}
}
inventree_data.status_text = "Found " + String(inventree_data.total_matches) + " item(s)"; inventree_data.status_text = "Found " + String(inventree_data.total_matches) + " item(s)";
setInventreeNote(inventree_data.total_matches > inventree_data.stored_matches setInventreeNote(inventree_data.total_matches > inventree_data.stored_matches
? "More items exist than shown here. Use the first loaded results." ? "More items exist than shown here. Use the first loaded results."
@@ -738,6 +1045,20 @@ void selectInventreeMatch(int delta)
inventree_data.selected_match = 0; inventree_data.selected_match = 0;
} }
String spool_error;
if (!loadSpoolWeightForMatch(inventree_data.matches[inventree_data.selected_match], spool_error))
{
Serial.printf("InvenTree spool weight fallback failed for part %d: %s\n",
inventree_data.matches[inventree_data.selected_match].part_id,
spool_error.c_str());
}
else
{
Serial.printf("InvenTree spool weight for part %d: %s\n",
inventree_data.matches[inventree_data.selected_match].part_id,
inventree_data.matches[inventree_data.selected_match].spool_weight.c_str());
}
updateInventreeSelectionSummary(); updateInventreeSelectionSummary();
} }
@@ -754,7 +1075,7 @@ void pushMeasuredWeightToInventree()
String quantity_text; String quantity_text;
if (!getMeasuredWeightForInventree(quantity_text)) if (!getMeasuredWeightForInventree(quantity_text))
{ {
setInventreeNote("Weight is not ready. Calibrate HX711 in grams first."); setInventreeNote("Filament Weight is not ready. Check calibration and Spool Weight.");
return; return;
} }
@@ -763,7 +1084,7 @@ void pushMeasuredWeightToInventree()
JsonObject item = items.add<JsonObject>(); JsonObject item = items.add<JsonObject>();
item["pk"] = inventree_data.matches[inventree_data.selected_match].id; item["pk"] = inventree_data.matches[inventree_data.selected_match].id;
item["quantity"] = quantity_text; item["quantity"] = quantity_text;
body_doc["notes"] = "Updated from FilamentGauger sensor"; body_doc["notes"] = "Updated net filament weight from FilamentGauger sensor";
String request_body; String request_body;
serializeJson(body_doc, request_body); serializeJson(body_doc, request_body);
@@ -776,7 +1097,7 @@ void pushMeasuredWeightToInventree()
{ {
inventree_data.status_text = "Update error"; inventree_data.status_text = "Update error";
inventree_data.result_text = error_text; inventree_data.result_text = error_text;
setInventreeNote("Could not submit measured weight to InvenTree."); setInventreeNote("Could not submit Filament Weight to InvenTree.");
updateInventreeLabels(); updateInventreeLabels();
return; return;
} }
@@ -785,17 +1106,17 @@ void pushMeasuredWeightToInventree()
{ {
inventree_data.status_text = "HTTP " + String(http_code); inventree_data.status_text = "HTTP " + String(http_code);
inventree_data.result_text = response.length() > 0 ? response : "Unexpected API response."; inventree_data.result_text = response.length() > 0 ? response : "Unexpected API response.";
setInventreeNote("Stock count update failed."); setInventreeNote("Filament Weight update failed.");
updateInventreeLabels(); updateInventreeLabels();
return; return;
} }
inventree_data.status_text = "Stock updated"; inventree_data.status_text = "Stock updated";
inventree_data.result_text = "Sent " + quantity_text + " g to stock item ID " + inventree_data.result_text = "Sent filament " + quantity_text + " g to stock item ID " +
String(inventree_data.matches[inventree_data.selected_match].id); String(inventree_data.matches[inventree_data.selected_match].id);
setInventreeNote("Measured weight was written to InvenTree stock quantity."); setInventreeNote("Filament Weight was written to InvenTree stock quantity.");
updateInventreeLabels(); updateInventreeLabels();
findInventreeStockByBatch(); findInventreeStockByBatch();
setInventreeNote("Measured weight was written to InvenTree stock quantity."); setInventreeNote("Filament Weight was written to InvenTree stock quantity.");
} }

View File

@@ -4,6 +4,7 @@ namespace
{ {
String serial_line; String serial_line;
constexpr lv_display_rotation_t kDisplayRotation = LV_DISPLAY_ROTATION_180;
void handleSerialCommand(const String &command) void handleSerialCommand(const String &command)
{ {
@@ -112,11 +113,17 @@ void setup()
hx711.begin(HX711_DOUT_PIN, HX711_SCK_PIN); hx711.begin(HX711_DOUT_PIN, HX711_SCK_PIN);
log_i("Initializing smart display...");
smartdisplay_init(); smartdisplay_init();
auto *display = lv_disp_get_default(); lv_display_t *display = lv_display_get_default();
lv_display_set_rotation(display, LV_DISPLAY_ROTATION_180); if (display != nullptr)
{
lv_display_set_rotation(display, kDisplayRotation);
}
log_i("Building UI...");
buildUi(); buildUi();
log_i("UI ready.");
lv_tick_inc(1); lv_tick_inc(1);
lv_timer_handler(); lv_timer_handler();
delay(20); delay(20);
@@ -134,6 +141,7 @@ void setup()
next_ui_refresh_ms = millis(); next_ui_refresh_ms = millis();
log_i("HX711 pins: SCK=%d DOUT=%d", HX711_SCK_PIN, HX711_DOUT_PIN); log_i("HX711 pins: SCK=%d DOUT=%d", HX711_SCK_PIN, HX711_DOUT_PIN);
log_i("Setup complete.");
} }
void loop() void loop()

View File

@@ -214,6 +214,7 @@ void openTextEditor(lv_obj_t *target, const char *title)
setLabelTextIfChanged(ui.text_editor_title, title != nullptr ? title : "Edit"); setLabelTextIfChanged(ui.text_editor_title, title != nullptr ? title : "Edit");
lv_textarea_set_password_mode(ui.text_editor_ta, lv_textarea_get_password_mode(target)); lv_textarea_set_password_mode(ui.text_editor_ta, lv_textarea_get_password_mode(target));
lv_textarea_set_accepted_chars(ui.text_editor_ta, batch_input ? "0123456789" : nullptr); lv_textarea_set_accepted_chars(ui.text_editor_ta, batch_input ? "0123456789" : nullptr);
lv_textarea_set_max_length(ui.text_editor_ta, batch_input ? kBatchCodeMaxLength : 0);
lv_textarea_set_text(ui.text_editor_ta, lv_textarea_get_text(target)); lv_textarea_set_text(ui.text_editor_ta, lv_textarea_get_text(target));
lv_keyboard_set_textarea(ui.wifi_keyboard, ui.text_editor_ta); lv_keyboard_set_textarea(ui.wifi_keyboard, ui.text_editor_ta);
lv_keyboard_set_mode(ui.wifi_keyboard, batch_input ? LV_KEYBOARD_MODE_USER_1 : LV_KEYBOARD_MODE_TEXT_LOWER); lv_keyboard_set_mode(ui.wifi_keyboard, batch_input ? LV_KEYBOARD_MODE_USER_1 : LV_KEYBOARD_MODE_TEXT_LOWER);
@@ -610,8 +611,45 @@ lv_obj_t *createTextInput(lv_obj_t *parent,
return textarea; return textarea;
} }
lv_obj_t *createValueField(lv_obj_t *parent,
lv_coord_t x,
lv_coord_t y,
lv_coord_t width,
const char *value_text)
{
lv_obj_t *field = lv_obj_create(parent);
lv_obj_set_size(field, width, 46);
lv_obj_set_pos(field, x, y);
lv_obj_remove_flag(field, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_radius(field, 14, 0);
lv_obj_set_style_bg_color(field, lv_color_black(), 0);
lv_obj_set_style_bg_opa(field, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(field, lv_color_hex(0x1A1A1A), 0);
lv_obj_set_style_border_width(field, 1, 0);
lv_obj_set_style_shadow_width(field, 0, 0);
lv_obj_set_style_pad_hor(field, 14, 0);
lv_obj_set_style_pad_ver(field, 10, 0);
lv_obj_t *value_label = lv_label_create(field);
lv_obj_set_width(value_label, width - 28);
lv_obj_set_style_text_color(value_label, lv_color_hex(0xF5FAFC), 0);
lv_obj_set_style_text_font(value_label, &lv_font_montserrat_18, 0);
lv_label_set_long_mode(value_label, LV_LABEL_LONG_WRAP);
lv_label_set_text(value_label, value_text != nullptr ? value_text : "--");
lv_obj_align(value_label, LV_ALIGN_LEFT_MID, 0, 0);
return value_label;
}
void buildWeightPage() void buildWeightPage()
{ {
constexpr lv_coord_t kBatchFieldWidth = 116;
constexpr lv_coord_t kFieldGap = 12;
constexpr lv_coord_t kSpoolFieldX = 24 + kBatchFieldWidth + kFieldGap;
constexpr lv_coord_t kSpoolFieldWidth = 210;
constexpr lv_coord_t kFilamentFieldX = kSpoolFieldX + kSpoolFieldWidth + kFieldGap;
constexpr lv_coord_t kFilamentFieldWidth = 210;
lv_obj_t *page = createPageContainer(ui.content_panel); lv_obj_t *page = createPageContainer(ui.content_panel);
ui.pages[pageIndex(PageId::Weight)] = page; ui.pages[pageIndex(PageId::Weight)] = page;
createPageHeader(page, "Main", "Weigh filament spools by Batch Code and write grams to InvenTree"); createPageHeader(page, "Main", "Weigh filament spools by Batch Code and write grams to InvenTree");
@@ -655,13 +693,30 @@ void buildWeightPage()
lv_obj_set_style_text_font(batch_label, &lv_font_montserrat_14, 0); lv_obj_set_style_text_font(batch_label, &lv_font_montserrat_14, 0);
lv_label_set_text(batch_label, "Batch Code"); lv_label_set_text(batch_label, "Batch Code");
ui.workflow_batch_ta = createTextInput(page, 24, 234, kContentInnerWidth, "Main Batch Code", "Scan or type Batch Code", false); ui.workflow_batch_ta = createTextInput(page, 24, 234, kBatchFieldWidth, "Main Batch Code", "Code", false);
lv_textarea_set_accepted_chars(ui.workflow_batch_ta, "0123456789"); lv_textarea_set_accepted_chars(ui.workflow_batch_ta, "0123456789");
lv_textarea_set_max_length(ui.workflow_batch_ta, kBatchCodeMaxLength);
if (!inventree_data.batch.isEmpty()) if (!inventree_data.batch.isEmpty())
{ {
lv_textarea_set_text(ui.workflow_batch_ta, inventree_data.batch.c_str()); lv_textarea_set_text(ui.workflow_batch_ta, inventree_data.batch.c_str());
} }
lv_obj_t *spool_weight_label = lv_label_create(page);
lv_obj_set_pos(spool_weight_label, kSpoolFieldX, 210);
lv_obj_set_style_text_color(spool_weight_label, lv_color_hex(0x7FA1B2), 0);
lv_obj_set_style_text_font(spool_weight_label, &lv_font_montserrat_14, 0);
lv_label_set_text(spool_weight_label, "Spool Weight");
ui.workflow_spool_weight_value = createValueField(page, kSpoolFieldX, 234, kSpoolFieldWidth, "--");
lv_obj_t *filament_weight_label = lv_label_create(page);
lv_obj_set_pos(filament_weight_label, kFilamentFieldX, 210);
lv_obj_set_style_text_color(filament_weight_label, lv_color_hex(0x7FA1B2), 0);
lv_obj_set_style_text_font(filament_weight_label, &lv_font_montserrat_14, 0);
lv_label_set_text(filament_weight_label, "Filament Weight");
ui.workflow_filament_weight_value = createValueField(page, kFilamentFieldX, 234, kFilamentFieldWidth, "--");
lv_obj_t *stock_title = lv_label_create(page); lv_obj_t *stock_title = lv_label_create(page);
lv_obj_set_pos(stock_title, 24, 290); lv_obj_set_pos(stock_title, 24, 290);
lv_obj_set_style_text_color(stock_title, lv_color_hex(0x7FA1B2), 0); lv_obj_set_style_text_color(stock_title, lv_color_hex(0x7FA1B2), 0);
@@ -685,7 +740,7 @@ void buildWeightPage()
ui.weight_note_label = lv_label_create(page); ui.weight_note_label = lv_label_create(page);
styleCenteredText(ui.weight_note_label, kContentInnerWidth, &lv_font_montserrat_14, lv_color_hex(0x8FA3AC)); styleCenteredText(ui.weight_note_label, kContentInnerWidth, &lv_font_montserrat_14, lv_color_hex(0x8FA3AC));
lv_label_set_long_mode(ui.weight_note_label, LV_LABEL_LONG_WRAP); lv_label_set_long_mode(ui.weight_note_label, LV_LABEL_LONG_WRAP);
lv_label_set_text(ui.weight_note_label, "Enter Batch Code, confirm input, then tap Write Weight."); lv_label_set_text(ui.weight_note_label, "Enter Batch Code, confirm input, verify Spool and Filament Weight, then tap Write Weight.");
alignTopCenter(ui.weight_note_label, 422); alignTopCenter(ui.weight_note_label, 422);
} }
@@ -800,6 +855,7 @@ void buildInventreePage()
ui.inventree_batch_ta = createTextInput(page, kLeftColumnX, 276, kColumnWidth, "InvenTree Batch Code", "Batch Code", false); ui.inventree_batch_ta = createTextInput(page, kLeftColumnX, 276, kColumnWidth, "InvenTree Batch Code", "Batch Code", false);
lv_textarea_set_accepted_chars(ui.inventree_batch_ta, "0123456789"); lv_textarea_set_accepted_chars(ui.inventree_batch_ta, "0123456789");
lv_textarea_set_max_length(ui.inventree_batch_ta, kBatchCodeMaxLength);
if (!inventree_data.batch.isEmpty()) if (!inventree_data.batch.isEmpty())
{ {
lv_textarea_set_text(ui.inventree_batch_ta, inventree_data.batch.c_str()); lv_textarea_set_text(ui.inventree_batch_ta, inventree_data.batch.c_str());
@@ -978,6 +1034,21 @@ void refreshUi()
setLabelTextIfChanged(ui.mode_label, ""); setLabelTextIfChanged(ui.mode_label, "");
setWeightStatus("RAW", lv_color_hex(0x1F5E7A)); setWeightStatus("RAW", lv_color_hex(0x1F5E7A));
} }
if (ui.workflow_filament_weight_value != nullptr)
{
int32_t filament_grams = 0;
if (getFilamentWeightGrams(filament_grams))
{
char buffer[24];
snprintf(buffer, sizeof(buffer), "%ld g", static_cast<long>(filament_grams));
setLabelTextIfChanged(ui.workflow_filament_weight_value, buffer);
}
else
{
setLabelTextIfChanged(ui.workflow_filament_weight_value, "--");
}
}
} }
void setActivePage(PageId page) void setActivePage(PageId page)
@@ -1102,6 +1173,12 @@ void updateInventreeLabels()
meta += match.location; meta += match.location;
} }
if (!match.spool_weight.isEmpty())
{
meta += " | Spool ";
meta += match.spool_weight;
}
setLabelTextIfChanged(ui.workflow_stock_name_label, title.c_str()); setLabelTextIfChanged(ui.workflow_stock_name_label, title.c_str());
setLabelTextIfChanged(ui.workflow_stock_meta_label, meta.c_str()); setLabelTextIfChanged(ui.workflow_stock_meta_label, meta.c_str());
} }
@@ -1118,6 +1195,36 @@ void updateInventreeLabels()
"The device will load the stock item and prepare the current weight for writeback."); "The device will load the stock item and prepare the current weight for writeback.");
} }
} }
if (ui.workflow_spool_weight_value != nullptr)
{
const char *spool_weight_text = "--";
if (inventree_data.selected_match >= 0 && inventree_data.selected_match < inventree_data.stored_matches)
{
const String &spool_weight = inventree_data.matches[inventree_data.selected_match].spool_weight;
if (!spool_weight.isEmpty())
{
spool_weight_text = spool_weight.c_str();
}
}
setLabelTextIfChanged(ui.workflow_spool_weight_value, spool_weight_text);
}
if (ui.workflow_filament_weight_value != nullptr)
{
int32_t filament_grams = 0;
if (getFilamentWeightGrams(filament_grams))
{
char buffer[24];
snprintf(buffer, sizeof(buffer), "%ld g", static_cast<long>(filament_grams));
setLabelTextIfChanged(ui.workflow_filament_weight_value, buffer);
}
else
{
setLabelTextIfChanged(ui.workflow_filament_weight_value, "--");
}
}
} }
void buildUi() void buildUi()
@@ -1240,7 +1347,7 @@ void buildUi()
layoutTextEditor(); layoutTextEditor();
lv_screen_load(ui.screen); lv_screen_load(ui.screen);
setWeightNote("Enter Batch Code, confirm input, then tap Write Weight."); setWeightNote("Enter Batch Code, confirm input, verify Spool and Filament Weight, then tap Write Weight.");
setCalibrationNote("Empty the platform, tap Tare, place the 100 g reference weight, then tap Cal."); setCalibrationNote("Empty the platform, tap Tare, place the 100 g reference weight, then tap Cal.");
if (wifi_data.ssid.isEmpty()) if (wifi_data.ssid.isEmpty())