diff --git a/CYD-Klipper/platformio.ini b/CYD-Klipper/platformio.ini index 3969297..4d45bf8 100644 --- a/CYD-Klipper/platformio.ini +++ b/CYD-Klipper/platformio.ini @@ -18,6 +18,7 @@ lib_deps = bblanchon/ArduinoJson@^7.0.0 plageoj/UrlEncode@^1.0.1 erriez/ErriezCRC32 @ ^1.0.1 + knolleary/PubSubClient@^2.8 monitor_filters = esp32_exception_decoder build_flags = -DLV_CONF_PATH="../../../../src/conf/lv_conf.h" @@ -33,6 +34,7 @@ lib_deps = https://github.com/PaulStoffregen/XPT2046_Touchscreen.git bblanchon/ArduinoJson@^7.0.0 plageoj/UrlEncode@^1.0.1 + knolleary/PubSubClient@^2.8 [env:esp32-3248S035C] board = esp32-3248S035C @@ -43,6 +45,7 @@ lib_deps = https://github.com/OperatorB/gt911-arduino-fixed-reset.git bblanchon/ArduinoJson@^7.0.0 plageoj/UrlEncode@^1.0.1 + knolleary/PubSubClient@^2.8 [env:esp32-3248S035C-V] board = esp32-3248S035C-vertical @@ -53,6 +56,7 @@ lib_deps = https://github.com/OperatorB/gt911-arduino-fixed-reset.git bblanchon/ArduinoJson@^7.0.0 plageoj/UrlEncode@^1.0.1 + knolleary/PubSubClient@^2.8 [env:esp32-2432S024C-SD] board = esp32-2432S024C-smartdisplay @@ -79,5 +83,4 @@ board = esp32-4827S043C-smartdisplay board = esp32-4827S043C-smartdisplay [env:esp32-8048S043C-SD] -board = esp32-8048S043C-smartdisplay - +board = esp32-8048S043C-smartdisplay \ No newline at end of file diff --git a/CYD-Klipper/src/conf/global_config.cpp b/CYD-Klipper/src/conf/global_config.cpp index c1e0e4c..56c05b2 100644 --- a/CYD-Klipper/src/conf/global_config.cpp +++ b/CYD-Klipper/src/conf/global_config.cpp @@ -81,6 +81,7 @@ void global_config_add_new_printer() new_config->setup_complete = false; new_config->ip_configured = false; new_config->auth_configured = false; + new_config->printer_type = PrinterType::PrinterTypeNone; new_config->printer_name[0] = 0; new_config->klipper_host[0] = 0; diff --git a/CYD-Klipper/src/conf/global_config.h b/CYD-Klipper/src/conf/global_config.h index 2dcf3c1..5cfad49 100644 --- a/CYD-Klipper/src/conf/global_config.h +++ b/CYD-Klipper/src/conf/global_config.h @@ -21,10 +21,12 @@ enum { }; enum PrinterType { - PrinterTypeKlipper = 0, - PrinterTypeKlipperSerial = 1, - PrinterTypeBambu = 2, - PrinterTypeOctoprint = 3, + PrinterTypeNone = 0, + PrinterTypeKlipper = 1, + PrinterTypeKlipperSerial = 2, + PrinterTypeBambuLocal = 3, + PrinterTypeBambuCloud = 3, + PrinterTypeOctoprint = 4, }; typedef struct { @@ -49,7 +51,7 @@ typedef struct { char printer_name[25]; char klipper_host[65]; char klipper_auth[33]; - unsigned short klipper_port; + unsigned int klipper_port; unsigned char color_scheme; diff --git a/CYD-Klipper/src/core/bambu/bambu_printer_integration.cpp b/CYD-Klipper/src/core/bambu/bambu_printer_integration.cpp new file mode 100644 index 0000000..511b754 --- /dev/null +++ b/CYD-Klipper/src/core/bambu/bambu_printer_integration.cpp @@ -0,0 +1,371 @@ +#include "bambu_printer_integration.hpp" +#include +#include + +WiFiClientSecure wifi_client; +PubSubClient client(wifi_client); +BambuPrinter* current_printer = NULL; + +const char* COMMAND_FETCH_ALL = "{\"pushing\":{\"sequence_id\":\"0\",\"command\":\"pushall\",\"version\":1,\"push_target\":1}}"; +const char* COMMAND_LIGHTCTL = "{\"system\":{\"sequence_id\":\"0\",\"command\":\"ledctrl\",\"led_node\":\"%s\",\"led_mode\":\"%s\"}}"; +const char* COMMAND_SEND_GCODE = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"gcode_line\",\"param\":\"%s\"}}"; +const char* COMMAND_PRINT_STOP = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"stop\",\"param\":\"\"}}"; +const char* COMMAND_PRINT_PAUSE = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"pause\",\"param\":\"\"}}"; +const char* COMMAND_PRINT_RESUME = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"resume\",\"param\":\"\"}}"; +const char* COMMAND_FILAMENT_UNLOAD = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"unload_filament\"}}"; +const char* COMMAND_FILAMENT_LOAD_EXTERNAL = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"ams_change_filament\",\"target\":254,\"curr_temp\":215,\"tar_temp\":250}}"; +const char* COMMAND_AMS_CONTOL_DONE = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"ams_control\",\"param\":\"done\"}}"; +const char* COMMAND_AMS_CONTOL_RETRY = "{\"print\":{\"sequence_id\":\"0\",\"command\":\"ams_control\",\"param\":\"resume\"}}"; + +static void callback(char* topic, byte* payload, unsigned int length) +{ + if (current_printer != NULL) + { + current_printer->receive_data(payload, length); + } +} + +void BambuPrinter::receive_data(unsigned char* data, unsigned int length) +{ + data[length] = 0; + JsonDocument doc; + deserializeJson(doc, data); + parse_state(doc); +} + +bool BambuPrinter::publish_mqtt_command(const char* command) +{ + if (!client.connected()) + { + return false; + } + + char auth[48] = {0}; + sprintf(auth, "device/%s/request", printer_config->klipper_auth); + + return client.publish(auth, command); +} + +bool BambuPrinter::move_printer(const char* axis, float amount, bool relative) +{ + if (!printer_data.homed_axis || printer_data.state == PrinterStatePrinting) + return false; + + char gcode[64]; + const char* extra = (amount > 0) ? "+" : ""; + const char* start = ""; + const char* end = ""; + + if (relative) { + start = "G91\n"; + } + else { + start = "G90\n"; + } + + sprintf(gcode, "%sG1 %s%s%.3f F6000", start, axis, extra, amount); + send_gcode(gcode); + + return true; +} + +bool BambuPrinter::execute_feature(PrinterFeatures feature) +{ + switch (feature) + { + case PrinterFeatureHome: + return send_gcode("G28"); + case PrinterFeatureDisableSteppers: + return send_gcode("M18 X Y Z"); + case PrinterFeaturePause: + return publish_mqtt_command(COMMAND_PRINT_PAUSE); + case PrinterFeatureResume: + return publish_mqtt_command(COMMAND_PRINT_RESUME); + case PrinterFeatureStop: + return publish_mqtt_command(COMMAND_PRINT_STOP); + case PrinterFeatureExtrude: + if (printer_data.state == PrinterStatePrinting) + { + return false; + } + + return send_gcode("M83\nG1 E25 F300"); + case PrinterFeatureRetract: + if (printer_data.state == PrinterStatePrinting) + { + return false; + } + + return send_gcode("M83\nG1 E-25 F300"); + case PrinterFeatureCooldown: + return send_gcode("M104 S0\nM140 S0"); + case PrinterFeatureContinueError: + return publish_mqtt_command(COMMAND_AMS_CONTOL_DONE); + case PrinterFeatureRetryError: + return publish_mqtt_command(COMMAND_AMS_CONTOL_RETRY); + case PrinterFeatureIgnoreError: + ignore_error = last_error; + publish_mqtt_command(COMMAND_FETCH_ALL); + return true; + default: + LOG_F(("Unsupported printer feature %d", feature)); + } + + return false; +} + +bool BambuPrinter::connect() +{ + wifi_client.setInsecure(); + client.setBufferSize(4096); + client.setServer(printer_config->klipper_host, 8883); + current_printer = this; + client.setCallback(NULL); + char buff[10] = {0}; + sprintf(buff, "%d", printer_config->klipper_port); + if (!client.connect("id", "bblp", buff)) + { + LOG_LN(("Bambu: Wrong IP or LAN code.")); + return false; + } + + char auth[48] = {0}; + sprintf(auth, "device/%s/report", printer_config->klipper_auth); + + if (!client.subscribe(auth)) + { + LOG_LN(("Bambu: Wrong serial number.")); + return false; + } + + delay(100); + client.loop(); + + if (!client.connected()) + { + LOG_LN(("Bambu: Connection lost. Likely wrong serial number.")); + return false; + } + + client.setCallback(callback); + printer_data.state = PrinterState::PrinterStateIdle; + return publish_mqtt_command(COMMAND_FETCH_ALL); +} + +void BambuPrinter::disconnect() +{ + current_printer = NULL; + printer_data.state = PrinterState::PrinterStateOffline; + client.disconnect(); + client.setCallback(NULL); + client.setBufferSize(16); +} + +bool BambuPrinter::fetch() +{ + if (!client.connected()) + { + LOG_LN("Failed to fetch printer data: Not connected"); + return false; + } + + if (!client.loop()) + { + LOG_LN("Failed to fetch printer data: Fetching data failed"); + return false; + } + + return true; +} + +PrinterDataMinimal BambuPrinter::fetch_min() +{ + PrinterDataMinimal min = {}; + min.success = true; + min.state = PrinterState::PrinterStateIdle; + min.print_progress = 0; + min.power_devices = 0; + return min; +} + +const char * MACRO_UNLOAD = "Unload filament"; +const char * MACRO_LOAD = "Load filament (External)"; + +Macros BambuPrinter::get_macros() +{ + Macros macros = {0}; + macros.success = true; + macros.count = get_macros_count(); + macros.macros = (char **)malloc(sizeof(char *) * macros.count); + + macros.macros[0] = (char *)malloc(25); + strcpy(macros.macros[0], MACRO_LOAD); + + macros.macros[1] = (char *)malloc(16); + strcpy(macros.macros[1], MACRO_UNLOAD); + + return macros; +} + +int BambuPrinter::get_macros_count() +{ + return 2; +} + +bool BambuPrinter::execute_macro(const char* macro) +{ + if (strcmp(macro, MACRO_LOAD) == 0) + { + return publish_mqtt_command(COMMAND_FILAMENT_LOAD_EXTERNAL); + } + else if (strcmp(macro, MACRO_UNLOAD) == 0) + { + return publish_mqtt_command(COMMAND_FILAMENT_UNLOAD); + } + + return false; +} + +const char* WORK_LIGHT = "Work Light"; +const char* CHAMBER_LIGHT = "Chamber Light"; + +PowerDevices BambuPrinter::get_power_devices() +{ + PowerDevices power_devices = {0}; + power_devices.success = true; + int count = get_power_devices_count(); + + if (count == 0) + { + return power_devices; + } + + power_devices.power_devices = (char **)malloc(sizeof(char *) * count); + power_devices.power_states = (bool *)malloc(sizeof(bool) * count); + + if (work_light_available) + { + power_devices.power_devices[power_devices.count] = (char *)malloc(10 + 1); + strcpy(power_devices.power_devices[power_devices.count], WORK_LIGHT); + power_devices.power_states[power_devices.count] = work_light_on; + power_devices.count++; + } + + if (chamber_light_available) + { + power_devices.power_devices[power_devices.count] = (char *)malloc(13 + 1); + strcpy(power_devices.power_devices[power_devices.count], CHAMBER_LIGHT); + power_devices.power_states[power_devices.count] = chamber_light_on; + power_devices.count++; + } + + return power_devices; +} + +int BambuPrinter::get_power_devices_count() +{ + return (work_light_available ? 1 : 0) + (chamber_light_available ? 1 : 0); +} + +bool BambuPrinter::set_power_device_state(const char* device_name, bool state) +{ + char buff[128] = {0}; + const char* device; + + if (strcmp(device_name, WORK_LIGHT) == 0) + { + device = "work_light"; + } + else if (strcmp(device_name, CHAMBER_LIGHT) == 0) + { + device = "chamber_light"; + } + else + { + return false; + } + + sprintf(buff, COMMAND_LIGHTCTL, device, state ? "on" : "off"); + return publish_mqtt_command(buff); +} + +Files BambuPrinter::get_files() +{ + Files files = {0}; + return files; +} + +bool BambuPrinter::start_file(const char* filename) +{ + return false; +} + +Thumbnail BambuPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename) +{ + Thumbnail thumbnail = {0}; + return thumbnail; +} + +bool BambuPrinter::set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature) +{ + char gcode[64] = {0}; + + switch (device) + { + case PrinterTemperatureDeviceBed: + sprintf(gcode, "M140 S%d", temperature); + break; + case PrinterTemperatureDeviceNozzle1: + sprintf(gcode, "M104 S%d", temperature); + break; + default: + LOG_F(("Unknown temperature device %d was requested to heat to %.2f", device, temperature)); + return false; + } + + return send_gcode(gcode); +} + +bool BambuPrinter::send_gcode(const char* gcode, bool wait) +{ + char* buff = (char *)malloc(strlen(gcode) + 70); + sprintf(buff, COMMAND_SEND_GCODE, gcode); + return publish_mqtt_command(buff); +} + +BambuConnectionStatus connection_test_bambu(PrinterConfiguration* config) +{ + WiFiClientSecure connection_test_wifi_client; + PubSubClient connection_test_client(connection_test_wifi_client); + connection_test_wifi_client.setInsecure(); + connection_test_client.setServer(config->klipper_host, 8883); + char buff[10] = {0}; + sprintf(buff, "%d", config->klipper_port); + if (!connection_test_client.connect("id", "bblp", buff)) + { + LOG_LN(("Bambu: Wrong IP or LAN code.")); + return BambuConnectionStatus::BambuConnectFail; + } + + char auth[48] = {0}; + sprintf(auth, "device/%s/report", config->klipper_auth); + + if (!connection_test_client.subscribe(auth)) + { + LOG_LN(("Bambu: Wrong serial number.")); + return BambuConnectionStatus::BambuConnectSNFail; + } + + delay(100); + connection_test_client.loop(); + + if (!connection_test_client.connected()) + { + LOG_LN(("Bambu: Connection lost. Likely wrong serial number.")); + return BambuConnectionStatus::BambuConnectSNFail; + } + + connection_test_client.disconnect(); + LOG_LN("Bambu: Connection test successful!"); + return BambuConnectionStatus::BambuConnectOk; +} \ No newline at end of file diff --git a/CYD-Klipper/src/core/bambu/bambu_printer_integration.hpp b/CYD-Klipper/src/core/bambu/bambu_printer_integration.hpp new file mode 100644 index 0000000..924a784 --- /dev/null +++ b/CYD-Klipper/src/core/bambu/bambu_printer_integration.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include "../printer_integration.hpp" +#include + +class BambuPrinter : public BasePrinter +{ + private: + unsigned int last_error = 0; + unsigned int ignore_error = 0; + bool publish_mqtt_command(const char* command); + unsigned char speed_profile = 2; + unsigned long print_start; + + union { + struct { + bool chamber_light_available : 1; + bool chamber_light_on : 1; + bool work_light_available : 1; + bool work_light_on : 1; + }; + unsigned char bambu_misc; + }; + + protected: + void parse_state(JsonDocument& in); + + public: + BambuPrinter(int index) : BasePrinter(index) + { + supported_features = PrinterFeatureHome + | PrinterFeatureDisableSteppers + | PrinterFeaturePause + | PrinterFeatureResume + | PrinterFeatureStop + | PrinterFeatureEmergencyStop + | PrinterFeatureCooldown + | PrinterFeatureContinueError + | PrinterFeatureExtrude + | PrinterFeatureRetract + | PrinterFeatureIgnoreError + | PrinterFeatureRetryError; + + supported_temperature_devices = PrinterTemperatureDeviceBed + | PrinterTemperatureDeviceNozzle1; + + popup_message_timeout_s = -1; + bambu_misc = 0; + printer_data.error_screen_features = PrinterFeatureRetryError | PrinterFeatureIgnoreError | PrinterFeatureContinueError; + } + + bool move_printer(const char* axis, float amount, bool relative); + bool execute_feature(PrinterFeatures feature); + bool connect(); + bool fetch(); + PrinterDataMinimal fetch_min(); + void disconnect(); + Macros get_macros(); + int get_macros_count(); + bool execute_macro(const char* macro); + PowerDevices get_power_devices(); + int get_power_devices_count(); + bool set_power_device_state(const char* device_name, bool state); + Files get_files(); + bool start_file(const char* filename); + Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename); + bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature); + bool send_gcode(const char* gcode, bool wait = true); + void receive_data(unsigned char* data, unsigned int length); +}; + +enum BambuConnectionStatus { + BambuConnectFail = 0, + BambuConnectOk = 1, + BambuConnectSNFail = 2, +}; + +BambuConnectionStatus connection_test_bambu(PrinterConfiguration* config); \ No newline at end of file diff --git a/CYD-Klipper/src/core/bambu/bambu_printer_parsers.cpp b/CYD-Klipper/src/core/bambu/bambu_printer_parsers.cpp new file mode 100644 index 0000000..01528cc --- /dev/null +++ b/CYD-Klipper/src/core/bambu/bambu_printer_parsers.cpp @@ -0,0 +1,207 @@ +#include "bambu_printer_integration.hpp" +#include + +#define BIT_X_AXIS_HOMED BIT(0) +#define BIT_Y_AXIS_HOMED BIT(1) +#define BIT_Z_AXIS_HOMED BIT(2) + +void BambuPrinter::parse_state(JsonDocument& in) +{ + if (!in.containsKey("print")) + { + return; + } + + auto print = in["print"]; + + if (print.containsKey("print_error")) + { + unsigned int error = print["print_error"].as(); + if (error != last_error) + { + last_error = error; + + if (error > 0) + { + HTTPClient client; + client.setTimeout(1000); + client.setConnectTimeout(1000); + + LOG_F(("Free heap: %d bytes\n", esp_get_free_heap_size())) + + char buff[10] = {0}; + sprintf(buff, "%X_%X", error >> 16, error & 0xFFFF); + int http_status_code = 0; + + try + { + client.begin("http://bambu.suchmeme.nl/" + String(buff)); + LOG_F(("Sending request to http://bambu.suchmeme.nl/%s", buff)); + http_status_code = client.GET(); + LOG_F(("Response: %d", http_status_code)); + } + catch (...) + { + LOG_LN("Error downloading error code page"); + } + + if (http_status_code == 200) + { + printer_data.state_message = (char *)malloc(client.getSize() + 20); + sprintf(printer_data.state_message, "%s: %s", buff, client.getString().c_str()); + } + else + { + printer_data.state_message = (char *)malloc(20); + sprintf(printer_data.state_message, "Error: %s", buff); + } + } + } + } + + if (print.containsKey("nozzle_temper")) + { + printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] = print["nozzle_temper"]; + printer_data.can_extrude = printer_data.temperatures[PrinterTemperatureDeviceIndexNozzle1] > 175; + } + + if (print.containsKey("nozzle_target_temper")) + { + printer_data.target_temperatures[PrinterTemperatureDeviceIndexNozzle1] = print["nozzle_target_temper"]; + } + + if (print.containsKey("bed_temper")) + { + printer_data.temperatures[PrinterTemperatureDeviceIndexBed] = print["bed_temper"]; + } + + if (print.containsKey("bed_target_temper")) + { + printer_data.target_temperatures[PrinterTemperatureDeviceIndexBed] = print["bed_target_temper"]; + } + + if (print.containsKey("spd_lvl")) + { + speed_profile = print["spd_lvl"]; + + switch (speed_profile) + { + case 1: + printer_data.speed_mult = 0.5f; + break; + case 2: + printer_data.speed_mult = 1.0f; + break; + case 3: + printer_data.speed_mult = 1.24f; + break; + case 4: + printer_data.speed_mult = 1.66f; + } + } + + if (print.containsKey("home_flag")) + { + unsigned int home_flag = print["home_flag"].as(); + printer_data.homed_axis = (home_flag & (BIT_X_AXIS_HOMED | BIT_Y_AXIS_HOMED | BIT_Z_AXIS_HOMED)) == (BIT_X_AXIS_HOMED | BIT_Y_AXIS_HOMED | BIT_Z_AXIS_HOMED); + } + + if (last_error > 0 && last_error != ignore_error) + { + printer_data.state = PrinterState::PrinterStateError; + } + else if (print.containsKey("gcode_state")) + { + const char* state = print["gcode_state"]; + + if (strcasecmp(state, "pause") == 0) + { + printer_data.state = PrinterState::PrinterStatePaused; + } + else if (strcasecmp(state, "running") == 0 || strcasecmp(state, "prepare") == 0) + { + if (print_start <= 0) + { + print_start = millis(); + } + + printer_data.state = PrinterState::PrinterStatePrinting; + } + else + { + print_start = 0; + printer_data.state = PrinterState::PrinterStateIdle; + } + } + + + + if (printer_data.state == PrinterState::PrinterStatePrinting) + { + printer_data.elapsed_time_s = (millis() - print_start) / 1000; + } + + if (print.containsKey("mc_remaining_time")) + { + printer_data.remaining_time_s = print["mc_remaining_time"]; + printer_data.remaining_time_s *= 60; + } + + if (print.containsKey("mc_percent")) + { + printer_data.print_progress = print["mc_percent"]; + printer_data.print_progress /= 100; + } + + if (print.containsKey("layer_num")) + { + printer_data.current_layer = print["layer_num"]; + } + + if (print.containsKey("total_layer_num")) + { + printer_data.total_layers = print["total_layer_num"]; + } + + if (print.containsKey("lights_report")) + { + for (auto lights : print["lights_report"].as()) + { + if (lights.containsKey("node") && lights.containsKey("mode")) + { + bool mode = !(lights["mode"] == "off"); + const char* node = lights["node"]; + + if (node == NULL) + { + continue; + } + + if (strcmp(node, "chamber_light") == 0) + { + chamber_light_available = true; + chamber_light_on = mode; + } + else if (strcmp(node, "work_light") == 0) + { + work_light_available = true; + work_light_on = mode; + } + } + } + } + + if (print.containsKey("gcode_file")) + { + const char* filename = print["gcode_file"]; + + if (filename != NULL && (printer_data.print_filename == NULL || strcmp(printer_data.print_filename, filename))) + { + printer_data.print_filename = (char *)malloc(strlen(filename) + 1); + strcpy(printer_data.print_filename, filename); + } + } + + printer_data.extrude_mult = 1; +} + diff --git a/CYD-Klipper/src/core/data_setup.cpp b/CYD-Klipper/src/core/data_setup.cpp index 4cdb4d6..0e948e9 100644 --- a/CYD-Klipper/src/core/data_setup.cpp +++ b/CYD-Klipper/src/core/data_setup.cpp @@ -4,6 +4,7 @@ #include #include "printer_integration.hpp" #include "klipper/klipper_printer_integration.hpp" +#include "bambu/bambu_printer_integration.hpp" SemaphoreHandle_t freezeRenderThreadSemaphore, freezeRequestThreadSemaphore; const long data_update_interval = 780; @@ -112,6 +113,8 @@ void data_setup() { case PrinterType::PrinterTypeKlipper: available_printers[count++] = new KlipperPrinter(i); + case PrinterType::PrinterTypeBambuLocal: + available_printers[count++] = new BambuPrinter(i); } } } diff --git a/CYD-Klipper/src/core/klipper/klipper_printer_integration.cpp b/CYD-Klipper/src/core/klipper/klipper_printer_integration.cpp index 9b91c34..c7acac9 100644 --- a/CYD-Klipper/src/core/klipper/klipper_printer_integration.cpp +++ b/CYD-Klipper/src/core/klipper/klipper_printer_integration.cpp @@ -105,8 +105,6 @@ bool KlipperPrinter::send_emergency_stop() bool KlipperPrinter::execute_feature(PrinterFeatures feature) { - HTTPClient client; - switch (feature) { case PrinterFeatureRestart: @@ -168,7 +166,7 @@ bool KlipperPrinter::execute_feature(PrinterFeatures feature) bool KlipperPrinter::connect() { - return connection_test_klipper(printer_config) == ConnectionStatus::ConnectOk; + return connection_test_klipper(printer_config) == KlipperConnectionStatus::ConnectOk; } bool KlipperPrinter::fetch() @@ -486,7 +484,7 @@ Thumbnail KlipperPrinter::get_32_32_png_image_thumbnail(const char* gcode_filena return thumbnail; } -ConnectionStatus connection_test_klipper(PrinterConfiguration* config) +KlipperConnectionStatus connection_test_klipper(PrinterConfiguration* config) { HTTPClient client; @@ -504,13 +502,13 @@ ConnectionStatus connection_test_klipper(PrinterConfiguration* config) if (http_code == 403) { - return ConnectionStatus::ConnectAuthRequired; + return KlipperConnectionStatus::ConnectAuthRequired; } - return http_code == 200 ? ConnectionStatus::ConnectOk : ConnectionStatus::ConnectFail; + return http_code == 200 ? KlipperConnectionStatus::ConnectOk : KlipperConnectionStatus::ConnectFail; } catch (...) { LOG_LN("Failed to connect"); - return ConnectionStatus::ConnectFail; + return KlipperConnectionStatus::ConnectFail; } } \ No newline at end of file diff --git a/CYD-Klipper/src/core/klipper/klipper_printer_integration.hpp b/CYD-Klipper/src/core/klipper/klipper_printer_integration.hpp index 758c9ba..9187280 100644 --- a/CYD-Klipper/src/core/klipper/klipper_printer_integration.hpp +++ b/CYD-Klipper/src/core/klipper/klipper_printer_integration.hpp @@ -55,6 +55,7 @@ class KlipperPrinter : public BasePrinter | PrinterTemperatureDeviceNozzle1; init_ui_panels(); + printer_data.error_screen_features = PrinterFeatureRestart | PrinterFeatureFirmwareRestart; } bool move_printer(const char* axis, float amount, bool relative); @@ -76,10 +77,10 @@ class KlipperPrinter : public BasePrinter bool send_gcode(const char* gcode, bool wait = true); }; -enum ConnectionStatus { +enum KlipperConnectionStatus { ConnectFail = 0, ConnectOk = 1, ConnectAuthRequired = 2, }; -ConnectionStatus connection_test_klipper(PrinterConfiguration* config); \ No newline at end of file +KlipperConnectionStatus connection_test_klipper(PrinterConfiguration* config); \ No newline at end of file diff --git a/CYD-Klipper/src/core/printer_integration.cpp b/CYD-Klipper/src/core/printer_integration.cpp index 8b9565f..05d1460 100644 --- a/CYD-Klipper/src/core/printer_integration.cpp +++ b/CYD-Klipper/src/core/printer_integration.cpp @@ -5,6 +5,7 @@ static char blank[] = { '\0' }; static unsigned char current_printer_index = 0; +static unsigned char last_announced_printer_index = 0; static unsigned char total_printers; static BasePrinter** registered_printers; static PrinterDataMinimal* minimal_data_copy; @@ -24,7 +25,9 @@ PrinterData* BasePrinter::AnnouncePrinterData() char* old_print_filename = printer_data_copy->print_filename; char* old_popup_message = printer_data_copy->popup_message; PrinterState old_state = printer_data_copy->state; + bool no_free = current_printer_index != last_announced_printer_index; + last_announced_printer_index = current_printer_index; memcpy(printer_data_copy, &printer_data, sizeof(PrinterData)); if (printer_data_copy->state_message == NULL) @@ -42,13 +45,13 @@ PrinterData* BasePrinter::AnnouncePrinterData() printer_data_copy->popup_message = blank; } - if (old_state_message != printer_data_copy->state_message && old_state_message != NULL && old_state_message != blank) + if (old_state_message != printer_data_copy->state_message && old_state_message != NULL && old_state_message != blank && !no_free) { LOG_F(("Freeing state message '%s' (%x)\n", old_state_message, old_state_message)); free(old_state_message); } - if (old_print_filename != printer_data_copy->print_filename && old_print_filename != NULL && old_print_filename != blank) + if (old_print_filename != printer_data_copy->print_filename && old_print_filename != NULL && old_print_filename != blank && !no_free) { LOG_F(("Freeing print filename '%s' (%x)\n", old_print_filename, old_print_filename)); free(old_print_filename); @@ -59,7 +62,7 @@ PrinterData* BasePrinter::AnnouncePrinterData() lv_msg_send(DATA_PRINTER_STATE, get_current_printer()); } - if (old_popup_message != printer_data_copy->popup_message && old_popup_message != NULL && old_popup_message != blank) + if (old_popup_message != printer_data_copy->popup_message && old_popup_message != NULL && old_popup_message != blank && !no_free) { LOG_F(("Freeing popup message '%s' (%x)\n", old_popup_message, old_popup_message)); free(old_popup_message); diff --git a/CYD-Klipper/src/core/printer_integration.hpp b/CYD-Klipper/src/core/printer_integration.hpp index 2e6b3cb..f90af31 100644 --- a/CYD-Klipper/src/core/printer_integration.hpp +++ b/CYD-Klipper/src/core/printer_integration.hpp @@ -13,9 +13,10 @@ enum PrinterFeatures { PrinterFeatureEmergencyStop = BIT(7), PrinterFeatureExtrude = BIT(8), PrinterFeatureRetract = BIT(9), - PrinterFeatureLoadFilament = BIT(10), - PrinterFeatureUnloadFilament = BIT(11), + PrinterFeatureIgnoreError = BIT(10), + PrinterFeatureContinueError = BIT(11), PrinterFeatureCooldown = BIT(12), + PrinterFeatureRetryError = BIT(13), }; inline PrinterFeatures operator|(PrinterFeatures a, PrinterFeatures b) @@ -78,7 +79,6 @@ typedef struct _PrinterData { char* popup_message; float temperatures[10]; float target_temperatures[10]; - PrinterTemperatureDevice AvailableDevices; float position[3]; float elapsed_time_s; float printed_time_s; @@ -94,6 +94,7 @@ typedef struct _PrinterData { float pressure_advance; float smooth_time; int feedrate_mm_per_s; + PrinterFeatures error_screen_features; } PrinterData; typedef struct { @@ -140,6 +141,8 @@ class BasePrinter PrinterData printer_data{}; public: + short popup_message_timeout_s = 10; + PrinterConfiguration* printer_config{}; PrinterFeatures supported_features{}; PrinterTemperatureDevice supported_temperature_devices{}; diff --git a/CYD-Klipper/src/ui/ip_setup.cpp b/CYD-Klipper/src/ui/ip_setup.cpp index e6990e3..0f2c27d 100644 --- a/CYD-Klipper/src/ui/ip_setup.cpp +++ b/CYD-Klipper/src/ui/ip_setup.cpp @@ -5,18 +5,17 @@ #include "core/data_setup.h" #include "ui_utils.h" #include "panels/panel.h" -#include "switch_printer.h" #include "macros.h" #include "../core/lv_setup.h" #include "serial/serial_console.h" #include "../core/klipper/klipper_printer_integration.hpp" - -lv_obj_t * hostEntry; -lv_obj_t * portEntry; -lv_obj_t * label = NULL; +#include "../core/bambu/bambu_printer_integration.hpp" +#include "../core/screen_driver.h" void show_ip_entry(); -void show_auth_entry(); +void choose_printer_type(); + +lv_obj_t * main_label; /* Create a custom keyboard to allow hostnames or ip addresses (a-z, 0 - 9, and -) */ static const char * kb_map[] = { @@ -34,10 +33,10 @@ static const lv_btnmatrix_ctrl_t kb_ctrl[] = { }; static const char * hex_numpad_map[] = { - "1", "2", "3", "f", LV_SYMBOL_BACKSPACE, "\n", - "4", "5", "6", "e", LV_SYMBOL_OK, "\n", - "7", "8", "9", "d", LV_SYMBOL_LEFT, "\n", - "0", "a", "b", "c", LV_SYMBOL_RIGHT, NULL + "1", "2", "3", "F", LV_SYMBOL_BACKSPACE, "\n", + "4", "5", "6", "E", LV_SYMBOL_OK, "\n", + "7", "8", "9", "D", LV_SYMBOL_LEFT, "\n", + "0", "A", "B", "C", LV_SYMBOL_RIGHT, NULL }; static const lv_btnmatrix_ctrl_t hex_numpad_ctrl[] = { @@ -47,6 +46,87 @@ static const lv_btnmatrix_ctrl_t hex_numpad_ctrl[] = { 1, 1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 1, }; +static void btn_switch_printer(lv_event_t *e){ + lv_obj_t *btn = lv_event_get_target(e); + PrinterConfiguration * config = (PrinterConfiguration*)lv_event_get_user_data(e); + int index = config - global_config.printer_config; + + global_config_set_printer(index); + set_color_scheme(); + set_invert_display(); + lv_obj_del(lv_obj_get_parent(lv_obj_get_parent(btn))); +} + +void switch_printer_init() { + lv_obj_t * parent = lv_create_empty_panel(lv_scr_act()); + lv_obj_set_style_bg_opa(parent, LV_OPA_100, 0); + lv_obj_align(parent, LV_ALIGN_TOP_RIGHT, 0, 0); + lv_obj_set_size(parent, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX); + lv_layout_flex_column(parent); + + lv_obj_set_size(lv_create_empty_panel(parent), 0, 0); + + auto width = CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2; + + lv_obj_t * btn = lv_btn_create(parent); + lv_obj_set_size(btn, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); + lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, parent); + + lv_obj_t * label = lv_label_create(btn); + lv_label_set_text(label, LV_SYMBOL_CLOSE " Close"); + lv_obj_center(label); + + for (int i = 0; i < PRINTER_CONFIG_COUNT; i++){ + PrinterConfiguration * config = &global_config.printer_config[i]; + const char* printer_name = (config->printer_name[0] == 0) ? config->klipper_host : config->printer_name; + + if (i == global_config.printer_index && config->setup_complete) + { + lv_create_custom_menu_label(printer_name, parent, "Active"); + continue; + } + + if (config->setup_complete) { + lv_create_custom_menu_button(printer_name, parent, btn_switch_printer, "Switch", config); + } + } +} + +static void show_switch_printer_screen(lv_event_t * e){ + switch_printer_init(); +} + +static void host_update(lv_event_t * e) +{ + lv_obj_t * ta = lv_event_get_target(e); + const char* text = lv_textarea_get_text(ta); + strcpy(global_config.printer_config[global_config.printer_index].klipper_host, text); + global_config.printer_config[global_config.printer_index].ip_configured = text[0] != '\0'; +} + +static void port_update(lv_event_t * e) +{ + lv_obj_t * ta = lv_event_get_target(e); + const char* text = lv_textarea_get_text(ta); + if (text[0] != '\0') + { + global_config.printer_config[global_config.printer_index].klipper_port = atoi(text); + } +} + +static void auth_update(lv_event_t * e) +{ + lv_obj_t * ta = lv_event_get_target(e); + const char* text = lv_textarea_get_text(ta); + strcpy(global_config.printer_config[global_config.printer_index].klipper_auth, text); + global_config.printer_config[global_config.printer_index].auth_configured = text[0] != '\0'; +} + +static void return_to_choose_printer_type(lv_event_t * e) +{ + choose_printer_type(); +} + static void keyboard_event_ip_entry(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); lv_obj_t * ta = lv_event_get_target(e); @@ -60,6 +140,10 @@ static void keyboard_event_ip_entry(lv_event_t * e) { { lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_USER_1); } + else if (lv_obj_has_flag(ta, LV_OBJ_FLAG_USER_2)) + { + lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_USER_2); + } else { lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_NUMBER); @@ -76,168 +160,243 @@ static void keyboard_event_ip_entry(lv_event_t * e) { } else if (code == LV_EVENT_READY) { - strcpy(global_config.printer_config[global_config.printer_index].klipper_host, lv_textarea_get_text(hostEntry)); - global_config.printer_config[global_config.printer_index].klipper_port = atoi(lv_textarea_get_text(portEntry)); + PrinterType type = global_config.printer_config[global_config.printer_index].printer_type; - ConnectionStatus status = connection_test_klipper(&global_config.printer_config[global_config.printer_index]); - if (status == ConnectionStatus::ConnectOk) - { - global_config.printer_config[global_config.printer_index].ip_configured = true; - global_config.printer_config[global_config.printer_index].setup_complete = true; - write_global_config(); - } - else if (status == ConnectionStatus::ConnectAuthRequired) + if (type == PrinterType::PrinterTypeKlipper) { - show_auth_entry(); - } - else - { - lv_label_set_text(label, "Failed to connect"); + KlipperConnectionStatus klipper_status = connection_test_klipper(&global_config.printer_config[global_config.printer_index]); + if (klipper_status == KlipperConnectionStatus::ConnectOk) + { + global_config.printer_config[global_config.printer_index].setup_complete = true; + write_global_config(); + } + else if (klipper_status == KlipperConnectionStatus::ConnectAuthRequired) + { + lv_label_set_text(main_label, "Incorrect authorisation"); + } + else + { + lv_label_set_text(main_label, "Failed to connect"); + } } - } - else - { - return; - } -} - -static void keyboard_event_auth_entry(lv_event_t * e) { - lv_event_code_t code = lv_event_get_code(e); - lv_obj_t * ta = lv_event_get_target(e); - lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e); - - if (code == LV_EVENT_READY) - { - const char * txt = lv_textarea_get_text(ta); - int len = strlen(txt); - if (len > 0) + else if (type == PrinterType::PrinterTypeBambuLocal) { - global_config.printer_config[global_config.printer_index].auth_configured = true; - strcpy(global_config.printer_config[global_config.printer_index].klipper_auth, txt); - - if (connection_test_klipper(&global_config.printer_config[global_config.printer_index]) == ConnectionStatus::ConnectOk) + BambuConnectionStatus bambu_status = connection_test_bambu(&global_config.printer_config[global_config.printer_index]); + if (bambu_status == BambuConnectionStatus::BambuConnectOk) { - global_config.printer_config[global_config.printer_index].ip_configured = true; global_config.printer_config[global_config.printer_index].setup_complete = true; write_global_config(); } - else + else if (bambu_status == BambuConnectionStatus::BambuConnectSNFail) + { + lv_label_set_text(main_label, "Incorrect serial number"); + } + else { - lv_label_set_text(label, "Failed to connect"); + lv_label_set_text(main_label, "Incorrect IP/Access code"); } } } - else if (code == LV_EVENT_CANCEL) + else { - show_ip_entry(); + return; } } -void show_auth_entry() +void show_ip_entry() { - global_config.printer_config[global_config.printer_index].klipper_auth[32] = 0; lv_obj_clean(lv_scr_act()); lv_obj_t * root = lv_create_empty_panel(lv_scr_act()); lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX); lv_layout_flex_column(root); + lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE); lv_obj_t * top_root = lv_create_empty_panel(root); lv_obj_set_width(top_root, CYD_SCREEN_WIDTH_PX); lv_layout_flex_column(top_root); lv_obj_set_flex_grow(top_root, 1); lv_obj_set_style_pad_all(top_root, CYD_SCREEN_GAP_PX, 0); + lv_obj_clear_flag(top_root, LV_OBJ_FLAG_SCROLLABLE); - label = lv_label_create(top_root); - lv_label_set_text(label, "Enter API Key"); - lv_obj_set_width(label, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2); + lv_obj_t * button_row = lv_create_empty_panel(top_root); + lv_obj_set_size(button_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT); + lv_layout_flex_row(button_row); - lv_obj_t * keyboard = lv_keyboard_create(root); - lv_obj_t * passEntry = lv_textarea_create(top_root); - lv_textarea_set_max_length(passEntry, 32); - lv_textarea_set_one_line(passEntry, true); + lv_obj_t * button_back = lv_btn_create(button_row); + lv_obj_set_height(button_back, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX / 2); + lv_obj_set_flex_grow(button_back, 1); + lv_obj_add_event_cb(button_back, return_to_choose_printer_type, LV_EVENT_CLICKED, NULL); - if (global_config.printer_config[global_config.printer_index].auth_configured) - lv_textarea_set_text(passEntry, global_config.printer_config[global_config.printer_index].klipper_auth); - else - lv_textarea_set_text(passEntry, ""); - - lv_obj_set_width(passEntry, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2); - lv_obj_add_event_cb(passEntry, keyboard_event_auth_entry, LV_EVENT_ALL, keyboard); - lv_obj_set_flex_grow(passEntry, 1); - lv_keyboard_set_textarea(keyboard, passEntry); - lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_2, hex_numpad_map, hex_numpad_ctrl); - lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_USER_2); -} + lv_obj_t * label = lv_label_create(button_back); + lv_label_set_text(label, LV_SYMBOL_LEFT); + lv_obj_center(label); -void show_ip_entry() -{ - lv_obj_clean(lv_scr_act()); + main_label = lv_label_create(button_row); - lv_obj_t * root = lv_create_empty_panel(lv_scr_act()); - lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX); - lv_layout_flex_column(root); + if (global_config.multi_printer_mode) + { + lv_obj_t * button_switch_printer = lv_btn_create(button_row); + lv_obj_set_height(button_switch_printer, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX / 2); + lv_obj_set_flex_grow(button_switch_printer, 1); + lv_obj_add_event_cb(button_switch_printer, show_switch_printer_screen, LV_EVENT_CLICKED, NULL); - lv_obj_t * top_root = lv_create_empty_panel(root); - lv_obj_set_width(top_root, CYD_SCREEN_WIDTH_PX); - lv_layout_flex_column(top_root); - lv_obj_set_flex_grow(top_root, 1); - lv_obj_set_style_pad_all(top_root, CYD_SCREEN_GAP_PX, 0); + label = lv_label_create(button_switch_printer); + lv_label_set_text(label, LV_SYMBOL_HOME); + lv_obj_center(label); + } - label = lv_label_create(top_root); - lv_label_set_text(label, "Enter Klipper IP/Hostname and Port"); - lv_obj_set_width(label, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2); + lv_obj_t * ip_row = lv_create_empty_panel(top_root); + lv_obj_set_size(ip_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT); + lv_layout_flex_row(ip_row); - lv_obj_t * textbow_row = lv_create_empty_panel(top_root); - lv_obj_set_width(textbow_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2); - lv_obj_set_flex_grow(textbow_row, 1); - lv_layout_flex_row(textbow_row); + lv_obj_t * host_entry = lv_textarea_create(ip_row); + lv_textarea_set_one_line(host_entry, true); + lv_obj_add_flag(host_entry, LV_OBJ_FLAG_USER_1); + lv_textarea_set_max_length(host_entry, 64); + lv_obj_set_flex_grow(host_entry, 2); - hostEntry = lv_textarea_create(textbow_row); - lv_textarea_set_one_line(hostEntry, true); - lv_obj_add_flag(hostEntry, LV_OBJ_FLAG_USER_1); - lv_textarea_set_max_length(hostEntry, 63); - lv_obj_set_flex_grow(hostEntry, 3); + lv_obj_t * port_entry = lv_textarea_create(ip_row); + lv_textarea_set_one_line(port_entry, true); + lv_obj_set_flex_grow(port_entry, 1); - portEntry = lv_textarea_create(textbow_row); - lv_textarea_set_one_line(portEntry, true); - lv_textarea_set_max_length(portEntry, 5); - - lv_obj_set_flex_grow(portEntry, 1); + lv_obj_t * auth_row = lv_create_empty_panel(top_root); + lv_obj_set_size(auth_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT); + lv_layout_flex_row(auth_row); + + lv_obj_t * auth_entry = lv_textarea_create(auth_row); + lv_textarea_set_one_line(auth_entry, true); + lv_obj_add_flag(auth_entry, LV_OBJ_FLAG_USER_2); + lv_obj_set_flex_grow(auth_entry, 1); + lv_textarea_set_max_length(auth_entry, 32); if (global_config.printer_config[global_config.printer_index].ip_configured) { - char buff[7] = {0}; + char buff[10] = {0}; sprintf(buff, "%d", global_config.printer_config[global_config.printer_index].klipper_port); - lv_textarea_set_text(hostEntry, global_config.printer_config[global_config.printer_index].klipper_host); - lv_textarea_set_text(portEntry, buff); + lv_textarea_set_text(host_entry, global_config.printer_config[global_config.printer_index].klipper_host); + lv_textarea_set_text(port_entry, buff); } else { - lv_textarea_set_text(hostEntry, ""); - lv_textarea_set_text(portEntry, "80"); + lv_textarea_set_text(host_entry, ""); + + if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeBambuLocal) + { + lv_textarea_set_text(port_entry, ""); + global_config.printer_config[global_config.printer_index].klipper_port = 0; + } + else + { + lv_textarea_set_text(port_entry, "80"); + global_config.printer_config[global_config.printer_index].klipper_port = 80; + } + + global_config.printer_config[global_config.printer_index].klipper_host[0] = '\0'; + + } + + if (global_config.printer_config[global_config.printer_index].auth_configured) + { + lv_textarea_set_text(auth_entry, global_config.printer_config[global_config.printer_index].klipper_auth); } + else + { + lv_textarea_set_text(auth_entry, ""); + global_config.printer_config[global_config.printer_index].klipper_auth[0] = '\0'; + } + + lv_obj_add_event_cb(host_entry, host_update, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(port_entry, port_update, LV_EVENT_VALUE_CHANGED, NULL); + lv_obj_add_event_cb(auth_entry, auth_update, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_t * keyboard = lv_keyboard_create(root); lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_1, kb_map, kb_ctrl); - lv_obj_add_event_cb(hostEntry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard); - lv_obj_add_event_cb(portEntry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard); + lv_keyboard_set_map(keyboard, LV_KEYBOARD_MODE_USER_2, hex_numpad_map, hex_numpad_ctrl); + lv_obj_add_event_cb(host_entry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard); + lv_obj_add_event_cb(port_entry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard); + lv_obj_add_event_cb(auth_entry, keyboard_event_ip_entry, LV_EVENT_ALL, keyboard); lv_keyboard_set_mode(keyboard, LV_KEYBOARD_MODE_USER_1); - lv_keyboard_set_textarea(keyboard, hostEntry); + lv_keyboard_set_textarea(keyboard, host_entry); - if (global_config.multi_printer_mode) + switch (global_config.printer_config[global_config.printer_index].printer_type) { - lv_obj_t * btn = draw_switch_printer_button(); - lv_obj_set_parent(btn, textbow_row); - lv_obj_align(btn, LV_ALIGN_DEFAULT, 0, 0); + case PrinterType::PrinterTypeKlipper: + lv_label_set_text(main_label, "Klipper Setup"); + lv_textarea_set_max_length(port_entry, 5); + lv_textarea_set_placeholder_text(host_entry, "Klipper host"); + lv_textarea_set_placeholder_text(port_entry, "Port"); + lv_textarea_set_placeholder_text(auth_entry, "Autorisation key (optional)"); + break; + case PrinterType::PrinterTypeBambuLocal: + lv_label_set_text(main_label, "Bambu (Local) Setup"); + lv_obj_set_flex_grow(port_entry, 4); + lv_obj_set_flex_grow(host_entry, 6); + lv_textarea_set_max_length(port_entry, 8); + lv_textarea_set_placeholder_text(host_entry, "Printer IP"); + lv_textarea_set_placeholder_text(port_entry, "Access code"); + lv_textarea_set_placeholder_text(auth_entry, "Printer serial number"); + break; } } +static void printer_type_klipper(lv_event_t * e) +{ + global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeKlipper; + show_ip_entry(); +} + +static void printer_type_bambu_local(lv_event_t * e) +{ + global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeBambuLocal; + show_ip_entry(); +} + +void choose_printer_type() +{ + lv_obj_clean(lv_scr_act()); + global_config.printer_config[global_config.printer_index].ip_configured = false; + global_config.printer_config[global_config.printer_index].auth_configured = false; + + lv_obj_t * root = lv_create_empty_panel(lv_scr_act()); + lv_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX); + lv_layout_flex_column(root); + lv_obj_set_flex_grow(root, 1); + lv_obj_set_style_pad_all(root, CYD_SCREEN_GAP_PX, 0); + lv_obj_clear_flag(root, LV_OBJ_FLAG_SCROLLABLE); + + lv_obj_t * label = lv_label_create(root); + lv_label_set_text(label, "Choose printer type"); + + lv_obj_t * btn = lv_btn_create(root); + lv_obj_set_size(btn, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); + lv_obj_add_event_cb(btn, printer_type_klipper, LV_EVENT_CLICKED, NULL); + + label = lv_label_create(btn); + lv_label_set_text(label, "Klipper"); + lv_obj_center(label); + + btn = lv_btn_create(root); + lv_obj_set_size(btn, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); + lv_obj_add_event_cb(btn, printer_type_bambu_local, LV_EVENT_CLICKED, NULL); + + label = lv_label_create(btn); + lv_label_set_text(label, "Bambu (Local)"); + lv_obj_center(label); +} + void ip_init(){ if (!global_config.printer_config[global_config.printer_index].setup_complete) { - show_ip_entry(); + if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeNone) + { + choose_printer_type(); + } + else + { + show_ip_entry(); + } } while (!global_config.printer_config[global_config.printer_index].setup_complete) diff --git a/CYD-Klipper/src/ui/main_ui.cpp b/CYD-Klipper/src/ui/main_ui.cpp index c18a300..31c49d7 100644 --- a/CYD-Klipper/src/ui/main_ui.cpp +++ b/CYD-Klipper/src/ui/main_ui.cpp @@ -8,7 +8,6 @@ #include "ui_utils.h" #include "panels/panel.h" #include "../core/lv_setup.h" -#include "switch_printer.h" #include "macros.h" void check_if_screen_needs_to_be_disabled(){ @@ -42,7 +41,7 @@ static void on_state_change(void * s, lv_msg_t * m){ static void on_popup_message(void * s, lv_msg_t * m) { - lv_create_popup_message(get_current_printer_data()->popup_message, 10000); + lv_create_popup_message(get_current_printer_data()->popup_message, get_current_printer()->popup_message_timeout_s * 1000); } void main_ui_setup(){ diff --git a/CYD-Klipper/src/ui/panels/error_panel.cpp b/CYD-Klipper/src/ui/panels/error_panel.cpp index 3641c4d..8272137 100644 --- a/CYD-Klipper/src/ui/panels/error_panel.cpp +++ b/CYD-Klipper/src/ui/panels/error_panel.cpp @@ -11,11 +11,34 @@ static void btn_click_firmware_restart(lv_event_t * e){ get_current_printer()->execute_feature(PrinterFeatureFirmwareRestart); } +static void btn_click_error_ignore(lv_event_t * e){ + get_current_printer()->execute_feature(PrinterFeatureIgnoreError); +} + +static void btn_click_error_continue(lv_event_t * e){ + get_current_printer()->execute_feature(PrinterFeatureContinueError); +} + +static void btn_click_error_retry(lv_event_t * e){ + get_current_printer()->execute_feature(PrinterFeatureRetryError); +} + static void set_state_message_text(lv_event_t * e) { lv_obj_t * label = lv_event_get_target(e); lv_label_set_text(label, get_current_printer_data()->state_message); } +void create_button(const char* label, lv_event_cb_t on_click, lv_obj_t * root){ + lv_obj_t* btn = lv_btn_create(root); + lv_obj_set_flex_grow(btn, 1); + lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); + lv_obj_add_event_cb(btn, on_click, LV_EVENT_CLICKED, NULL); + + lv_obj_t * label_obj = lv_label_create(btn); + lv_label_set_text(label_obj, label); + lv_obj_center(label_obj); +} + void error_panel_init(lv_obj_t* panel) { lv_layout_flex_column(panel, LV_FLEX_ALIGN_SPACE_BETWEEN); @@ -40,21 +63,28 @@ void error_panel_init(lv_obj_t* panel) lv_obj_set_size(button_row, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); lv_layout_flex_row(button_row); - lv_obj_t * btn = lv_btn_create(button_row); - lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); - lv_obj_add_event_cb(btn, btn_click_restart, LV_EVENT_CLICKED, NULL); - lv_obj_set_flex_grow(btn, 1); + if (get_current_printer_data()->error_screen_features & PrinterFeatureRestart) + { + create_button("Restart", btn_click_restart, button_row); + } - label = lv_label_create(btn); - lv_label_set_text(label, "Restart"); - lv_obj_center(label); + if (get_current_printer_data()->error_screen_features & PrinterFeatureFirmwareRestart) + { + create_button("FW Restart", btn_click_firmware_restart, button_row); + } - btn = lv_btn_create(button_row); - lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); - lv_obj_add_event_cb(btn, btn_click_firmware_restart, LV_EVENT_CLICKED, NULL); - lv_obj_set_flex_grow(btn, 1); + if (get_current_printer_data()->error_screen_features & PrinterFeatureIgnoreError) + { + create_button("Ignore", btn_click_error_ignore, button_row); + } + + if (get_current_printer_data()->error_screen_features & PrinterFeatureContinueError) + { + create_button("Continue", btn_click_error_continue, button_row); + } - label = lv_label_create(btn); - lv_label_set_text(label, "FW Restart"); - lv_obj_center(label); + if (get_current_printer_data()->error_screen_features & PrinterFeatureRetryError) + { + create_button("Retry", btn_click_error_retry, button_row); + } } \ No newline at end of file diff --git a/CYD-Klipper/src/ui/panels/printer_panel.cpp b/CYD-Klipper/src/ui/panels/printer_panel.cpp index 71e8778..5e22797 100644 --- a/CYD-Klipper/src/ui/panels/printer_panel.cpp +++ b/CYD-Klipper/src/ui/panels/printer_panel.cpp @@ -5,7 +5,6 @@ #include "../../core/lv_setup.h" #include #include "../nav_buttons.h" -#include "../switch_printer.h" #include "../macros.h" const char * printer_status[] = { diff --git a/CYD-Klipper/src/ui/serial/serial_commands.cpp b/CYD-Klipper/src/ui/serial/serial_commands.cpp index 7771b2d..8dfe71c 100644 --- a/CYD-Klipper/src/ui/serial/serial_commands.cpp +++ b/CYD-Klipper/src/ui/serial/serial_commands.cpp @@ -3,7 +3,6 @@ #include #include #include "../../conf/global_config.h" -#include "../switch_printer.h" #include "../../core/printer_integration.hpp" namespace serial_console { diff --git a/CYD-Klipper/src/ui/switch_printer.cpp b/CYD-Klipper/src/ui/switch_printer.cpp deleted file mode 100644 index be95674..0000000 --- a/CYD-Klipper/src/ui/switch_printer.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "switch_printer.h" -#include "../conf/global_config.h" -#include "ui_utils.h" -#include "../core/lv_setup.h" -#include "../core/screen_driver.h" - -static void btn_switch_printer(lv_event_t *e){ - lv_obj_t *btn = lv_event_get_target(e); - PrinterConfiguration * config = (PrinterConfiguration*)lv_event_get_user_data(e); - int index = config - global_config.printer_config; - - global_config_set_printer(index); - set_color_scheme(); - set_invert_display(); - lv_obj_del(lv_obj_get_parent(lv_obj_get_parent(btn))); -} - -void switch_printer_init() { - lv_obj_t * parent = lv_create_empty_panel(lv_scr_act()); - lv_obj_set_style_bg_opa(parent, LV_OPA_100, 0); - lv_obj_align(parent, LV_ALIGN_TOP_RIGHT, 0, 0); - lv_obj_set_size(parent, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX); - lv_layout_flex_column(parent); - - lv_obj_set_size(lv_create_empty_panel(parent), 0, 0); - - auto width = CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2; - - lv_obj_t * btn = lv_btn_create(parent); - lv_obj_set_size(btn, width, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); - lv_obj_add_event_cb(btn, destroy_event_user_data, LV_EVENT_CLICKED, parent); - - lv_obj_t * label = lv_label_create(btn); - lv_label_set_text(label, LV_SYMBOL_CLOSE " Close"); - lv_obj_center(label); - - for (int i = 0; i < PRINTER_CONFIG_COUNT; i++){ - PrinterConfiguration * config = &global_config.printer_config[i]; - const char* printer_name = (config->printer_name[0] == 0) ? config->klipper_host : config->printer_name; - - if (i == global_config.printer_index && config->setup_complete) - { - lv_create_custom_menu_label(printer_name, parent, "Active"); - continue; - } - - if (config->setup_complete) { - lv_create_custom_menu_button(printer_name, parent, btn_switch_printer, "Switch", config); - } - } -} - -static void show_switch_printer_screen(lv_event_t * e){ - switch_printer_init(); -} - -lv_obj_t * draw_switch_printer_button() -{ - if (!global_config.multi_printer_mode) - { - return NULL; - } - - lv_obj_t * btn = lv_btn_create(lv_scr_act()); - lv_obj_set_size(btn, CYD_SCREEN_MIN_BUTTON_WIDTH_PX, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); - lv_obj_align(btn, LV_ALIGN_TOP_RIGHT, -CYD_SCREEN_GAP_PX, CYD_SCREEN_GAP_PX); - lv_obj_add_event_cb(btn, show_switch_printer_screen, LV_EVENT_CLICKED, NULL); - - lv_obj_t * label = lv_label_create(btn); - lv_label_set_text(label, LV_SYMBOL_HOME); - lv_obj_center(label); - - return btn; -} \ No newline at end of file diff --git a/CYD-Klipper/src/ui/switch_printer.h b/CYD-Klipper/src/ui/switch_printer.h deleted file mode 100644 index 2926047..0000000 --- a/CYD-Klipper/src/ui/switch_printer.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "lvgl.h" - -void switch_printer_init(); -lv_obj_t * draw_switch_printer_button(); \ No newline at end of file diff --git a/CYD-Klipper/src/ui/ui_utils.cpp b/CYD-Klipper/src/ui/ui_utils.cpp index 3fcf4fc..3730fb7 100644 --- a/CYD-Klipper/src/ui/ui_utils.cpp +++ b/CYD-Klipper/src/ui/ui_utils.cpp @@ -269,7 +269,7 @@ void lv_create_popup_message(const char* message, uint16_t timeout_ms) lv_obj_set_size(label, CYD_SCREEN_PANEL_WIDTH_PX - CYD_SCREEN_GAP_PX * 6, LV_SIZE_CONTENT); lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); - timer = lv_timer_create(timer_callback, timeout_ms, panel); + timer = lv_timer_create(timer_callback, timeout_ms > 0 ? timeout_ms : 0xFFFFFFFF, panel); } lv_obj_t * lv_label_btn_create(lv_obj_t * parent, lv_event_cb_t btn_callback, void* user_data)