Basic bambu implementation

This commit is contained in:
suchmememanyskill
2024-10-29 17:57:20 +01:00
parent b1138d9510
commit 4207d82a7e
19 changed files with 1030 additions and 253 deletions

View File

@@ -18,6 +18,7 @@ lib_deps =
bblanchon/ArduinoJson@^7.0.0 bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1 plageoj/UrlEncode@^1.0.1
erriez/ErriezCRC32 @ ^1.0.1 erriez/ErriezCRC32 @ ^1.0.1
knolleary/PubSubClient@^2.8
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
build_flags = build_flags =
-DLV_CONF_PATH="../../../../src/conf/lv_conf.h" -DLV_CONF_PATH="../../../../src/conf/lv_conf.h"
@@ -33,6 +34,7 @@ lib_deps =
https://github.com/PaulStoffregen/XPT2046_Touchscreen.git https://github.com/PaulStoffregen/XPT2046_Touchscreen.git
bblanchon/ArduinoJson@^7.0.0 bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1 plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
[env:esp32-3248S035C] [env:esp32-3248S035C]
board = esp32-3248S035C board = esp32-3248S035C
@@ -43,6 +45,7 @@ lib_deps =
https://github.com/OperatorB/gt911-arduino-fixed-reset.git https://github.com/OperatorB/gt911-arduino-fixed-reset.git
bblanchon/ArduinoJson@^7.0.0 bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1 plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
[env:esp32-3248S035C-V] [env:esp32-3248S035C-V]
board = esp32-3248S035C-vertical board = esp32-3248S035C-vertical
@@ -53,6 +56,7 @@ lib_deps =
https://github.com/OperatorB/gt911-arduino-fixed-reset.git https://github.com/OperatorB/gt911-arduino-fixed-reset.git
bblanchon/ArduinoJson@^7.0.0 bblanchon/ArduinoJson@^7.0.0
plageoj/UrlEncode@^1.0.1 plageoj/UrlEncode@^1.0.1
knolleary/PubSubClient@^2.8
[env:esp32-2432S024C-SD] [env:esp32-2432S024C-SD]
board = esp32-2432S024C-smartdisplay board = esp32-2432S024C-smartdisplay
@@ -80,4 +84,3 @@ board = esp32-4827S043C-smartdisplay
[env:esp32-8048S043C-SD] [env:esp32-8048S043C-SD]
board = esp32-8048S043C-smartdisplay board = esp32-8048S043C-smartdisplay

View File

@@ -81,6 +81,7 @@ void global_config_add_new_printer()
new_config->setup_complete = false; new_config->setup_complete = false;
new_config->ip_configured = false; new_config->ip_configured = false;
new_config->auth_configured = false; new_config->auth_configured = false;
new_config->printer_type = PrinterType::PrinterTypeNone;
new_config->printer_name[0] = 0; new_config->printer_name[0] = 0;
new_config->klipper_host[0] = 0; new_config->klipper_host[0] = 0;

View File

@@ -21,10 +21,12 @@ enum {
}; };
enum PrinterType { enum PrinterType {
PrinterTypeKlipper = 0, PrinterTypeNone = 0,
PrinterTypeKlipperSerial = 1, PrinterTypeKlipper = 1,
PrinterTypeBambu = 2, PrinterTypeKlipperSerial = 2,
PrinterTypeOctoprint = 3, PrinterTypeBambuLocal = 3,
PrinterTypeBambuCloud = 3,
PrinterTypeOctoprint = 4,
}; };
typedef struct { typedef struct {
@@ -49,7 +51,7 @@ typedef struct {
char printer_name[25]; char printer_name[25];
char klipper_host[65]; char klipper_host[65];
char klipper_auth[33]; char klipper_auth[33];
unsigned short klipper_port; unsigned int klipper_port;
unsigned char color_scheme; unsigned char color_scheme;

View File

@@ -0,0 +1,371 @@
#include "bambu_printer_integration.hpp"
#include <PubSubClient.h>
#include <WifiClientSecure.h>
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;
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include "../printer_integration.hpp"
#include <ArduinoJson.h>
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);

View File

@@ -0,0 +1,207 @@
#include "bambu_printer_integration.hpp"
#include <HTTPClient.h>
#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<unsigned int>();
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<unsigned int>();
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<JsonArray>())
{
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;
}

View File

@@ -4,6 +4,7 @@
#include <UrlEncode.h> #include <UrlEncode.h>
#include "printer_integration.hpp" #include "printer_integration.hpp"
#include "klipper/klipper_printer_integration.hpp" #include "klipper/klipper_printer_integration.hpp"
#include "bambu/bambu_printer_integration.hpp"
SemaphoreHandle_t freezeRenderThreadSemaphore, freezeRequestThreadSemaphore; SemaphoreHandle_t freezeRenderThreadSemaphore, freezeRequestThreadSemaphore;
const long data_update_interval = 780; const long data_update_interval = 780;
@@ -112,6 +113,8 @@ void data_setup()
{ {
case PrinterType::PrinterTypeKlipper: case PrinterType::PrinterTypeKlipper:
available_printers[count++] = new KlipperPrinter(i); available_printers[count++] = new KlipperPrinter(i);
case PrinterType::PrinterTypeBambuLocal:
available_printers[count++] = new BambuPrinter(i);
} }
} }
} }

View File

@@ -105,8 +105,6 @@ bool KlipperPrinter::send_emergency_stop()
bool KlipperPrinter::execute_feature(PrinterFeatures feature) bool KlipperPrinter::execute_feature(PrinterFeatures feature)
{ {
HTTPClient client;
switch (feature) switch (feature)
{ {
case PrinterFeatureRestart: case PrinterFeatureRestart:
@@ -168,7 +166,7 @@ bool KlipperPrinter::execute_feature(PrinterFeatures feature)
bool KlipperPrinter::connect() bool KlipperPrinter::connect()
{ {
return connection_test_klipper(printer_config) == ConnectionStatus::ConnectOk; return connection_test_klipper(printer_config) == KlipperConnectionStatus::ConnectOk;
} }
bool KlipperPrinter::fetch() bool KlipperPrinter::fetch()
@@ -486,7 +484,7 @@ Thumbnail KlipperPrinter::get_32_32_png_image_thumbnail(const char* gcode_filena
return thumbnail; return thumbnail;
} }
ConnectionStatus connection_test_klipper(PrinterConfiguration* config) KlipperConnectionStatus connection_test_klipper(PrinterConfiguration* config)
{ {
HTTPClient client; HTTPClient client;
@@ -504,13 +502,13 @@ ConnectionStatus connection_test_klipper(PrinterConfiguration* config)
if (http_code == 403) 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 (...) { catch (...) {
LOG_LN("Failed to connect"); LOG_LN("Failed to connect");
return ConnectionStatus::ConnectFail; return KlipperConnectionStatus::ConnectFail;
} }
} }

View File

@@ -55,6 +55,7 @@ class KlipperPrinter : public BasePrinter
| PrinterTemperatureDeviceNozzle1; | PrinterTemperatureDeviceNozzle1;
init_ui_panels(); init_ui_panels();
printer_data.error_screen_features = PrinterFeatureRestart | PrinterFeatureFirmwareRestart;
} }
bool move_printer(const char* axis, float amount, bool relative); 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); bool send_gcode(const char* gcode, bool wait = true);
}; };
enum ConnectionStatus { enum KlipperConnectionStatus {
ConnectFail = 0, ConnectFail = 0,
ConnectOk = 1, ConnectOk = 1,
ConnectAuthRequired = 2, ConnectAuthRequired = 2,
}; };
ConnectionStatus connection_test_klipper(PrinterConfiguration* config); KlipperConnectionStatus connection_test_klipper(PrinterConfiguration* config);

View File

@@ -5,6 +5,7 @@
static char blank[] = { '\0' }; static char blank[] = { '\0' };
static unsigned char current_printer_index = 0; static unsigned char current_printer_index = 0;
static unsigned char last_announced_printer_index = 0;
static unsigned char total_printers; static unsigned char total_printers;
static BasePrinter** registered_printers; static BasePrinter** registered_printers;
static PrinterDataMinimal* minimal_data_copy; static PrinterDataMinimal* minimal_data_copy;
@@ -24,7 +25,9 @@ PrinterData* BasePrinter::AnnouncePrinterData()
char* old_print_filename = printer_data_copy->print_filename; char* old_print_filename = printer_data_copy->print_filename;
char* old_popup_message = printer_data_copy->popup_message; char* old_popup_message = printer_data_copy->popup_message;
PrinterState old_state = printer_data_copy->state; 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)); memcpy(printer_data_copy, &printer_data, sizeof(PrinterData));
if (printer_data_copy->state_message == NULL) if (printer_data_copy->state_message == NULL)
@@ -42,13 +45,13 @@ PrinterData* BasePrinter::AnnouncePrinterData()
printer_data_copy->popup_message = blank; 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)); LOG_F(("Freeing state message '%s' (%x)\n", old_state_message, old_state_message));
free(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)); LOG_F(("Freeing print filename '%s' (%x)\n", old_print_filename, old_print_filename));
free(old_print_filename); free(old_print_filename);
@@ -59,7 +62,7 @@ PrinterData* BasePrinter::AnnouncePrinterData()
lv_msg_send(DATA_PRINTER_STATE, get_current_printer()); 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)); LOG_F(("Freeing popup message '%s' (%x)\n", old_popup_message, old_popup_message));
free(old_popup_message); free(old_popup_message);

View File

@@ -13,9 +13,10 @@ enum PrinterFeatures {
PrinterFeatureEmergencyStop = BIT(7), PrinterFeatureEmergencyStop = BIT(7),
PrinterFeatureExtrude = BIT(8), PrinterFeatureExtrude = BIT(8),
PrinterFeatureRetract = BIT(9), PrinterFeatureRetract = BIT(9),
PrinterFeatureLoadFilament = BIT(10), PrinterFeatureIgnoreError = BIT(10),
PrinterFeatureUnloadFilament = BIT(11), PrinterFeatureContinueError = BIT(11),
PrinterFeatureCooldown = BIT(12), PrinterFeatureCooldown = BIT(12),
PrinterFeatureRetryError = BIT(13),
}; };
inline PrinterFeatures operator|(PrinterFeatures a, PrinterFeatures b) inline PrinterFeatures operator|(PrinterFeatures a, PrinterFeatures b)
@@ -78,7 +79,6 @@ typedef struct _PrinterData {
char* popup_message; char* popup_message;
float temperatures[10]; float temperatures[10];
float target_temperatures[10]; float target_temperatures[10];
PrinterTemperatureDevice AvailableDevices;
float position[3]; float position[3];
float elapsed_time_s; float elapsed_time_s;
float printed_time_s; float printed_time_s;
@@ -94,6 +94,7 @@ typedef struct _PrinterData {
float pressure_advance; float pressure_advance;
float smooth_time; float smooth_time;
int feedrate_mm_per_s; int feedrate_mm_per_s;
PrinterFeatures error_screen_features;
} PrinterData; } PrinterData;
typedef struct { typedef struct {
@@ -140,6 +141,8 @@ class BasePrinter
PrinterData printer_data{}; PrinterData printer_data{};
public: public:
short popup_message_timeout_s = 10;
PrinterConfiguration* printer_config{}; PrinterConfiguration* printer_config{};
PrinterFeatures supported_features{}; PrinterFeatures supported_features{};
PrinterTemperatureDevice supported_temperature_devices{}; PrinterTemperatureDevice supported_temperature_devices{};

View File

@@ -5,18 +5,17 @@
#include "core/data_setup.h" #include "core/data_setup.h"
#include "ui_utils.h" #include "ui_utils.h"
#include "panels/panel.h" #include "panels/panel.h"
#include "switch_printer.h"
#include "macros.h" #include "macros.h"
#include "../core/lv_setup.h" #include "../core/lv_setup.h"
#include "serial/serial_console.h" #include "serial/serial_console.h"
#include "../core/klipper/klipper_printer_integration.hpp" #include "../core/klipper/klipper_printer_integration.hpp"
#include "../core/bambu/bambu_printer_integration.hpp"
lv_obj_t * hostEntry; #include "../core/screen_driver.h"
lv_obj_t * portEntry;
lv_obj_t * label = NULL;
void show_ip_entry(); 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 -) */ /* Create a custom keyboard to allow hostnames or ip addresses (a-z, 0 - 9, and -) */
static const char * kb_map[] = { static const char * kb_map[] = {
@@ -34,10 +33,10 @@ static const lv_btnmatrix_ctrl_t kb_ctrl[] = {
}; };
static const char * hex_numpad_map[] = { static const char * hex_numpad_map[] = {
"1", "2", "3", "f", LV_SYMBOL_BACKSPACE, "\n", "1", "2", "3", "F", LV_SYMBOL_BACKSPACE, "\n",
"4", "5", "6", "e", LV_SYMBOL_OK, "\n", "4", "5", "6", "E", LV_SYMBOL_OK, "\n",
"7", "8", "9", "d", LV_SYMBOL_LEFT, "\n", "7", "8", "9", "D", LV_SYMBOL_LEFT, "\n",
"0", "a", "b", "c", LV_SYMBOL_RIGHT, NULL "0", "A", "B", "C", LV_SYMBOL_RIGHT, NULL
}; };
static const lv_btnmatrix_ctrl_t hex_numpad_ctrl[] = { 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, 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) { static void keyboard_event_ip_entry(lv_event_t * e) {
lv_event_code_t code = lv_event_get_code(e); lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target(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); 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 else
{ {
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_NUMBER); lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_NUMBER);
@@ -76,23 +160,41 @@ static void keyboard_event_ip_entry(lv_event_t * e) {
} }
else if (code == LV_EVENT_READY) else if (code == LV_EVENT_READY)
{ {
strcpy(global_config.printer_config[global_config.printer_index].klipper_host, lv_textarea_get_text(hostEntry)); PrinterType type = global_config.printer_config[global_config.printer_index].printer_type;
global_config.printer_config[global_config.printer_index].klipper_port = atoi(lv_textarea_get_text(portEntry));
ConnectionStatus status = connection_test_klipper(&global_config.printer_config[global_config.printer_index]); if (type == PrinterType::PrinterTypeKlipper)
if (status == ConnectionStatus::ConnectOk) {
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].ip_configured = true;
global_config.printer_config[global_config.printer_index].setup_complete = true; global_config.printer_config[global_config.printer_index].setup_complete = true;
write_global_config(); write_global_config();
} }
else if (status == ConnectionStatus::ConnectAuthRequired) else if (klipper_status == KlipperConnectionStatus::ConnectAuthRequired)
{ {
show_auth_entry(); lv_label_set_text(main_label, "Incorrect authorisation");
} }
else else
{ {
lv_label_set_text(label, "Failed to connect"); lv_label_set_text(main_label, "Failed to connect");
}
}
else if (type == PrinterType::PrinterTypeBambuLocal)
{
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].setup_complete = true;
write_global_config();
}
else if (bambu_status == BambuConnectionStatus::BambuConnectSNFail)
{
lv_label_set_text(main_label, "Incorrect serial number");
}
else
{
lv_label_set_text(main_label, "Incorrect IP/Access code");
}
} }
} }
else else
@@ -101,76 +203,6 @@ static void keyboard_event_ip_entry(lv_event_t * e) {
} }
} }
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)
{
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)
{
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
{
lv_label_set_text(label, "Failed to connect");
}
}
}
else if (code == LV_EVENT_CANCEL)
{
show_ip_entry();
}
}
void show_auth_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_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(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 * 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);
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);
}
void show_ip_entry() void show_ip_entry()
{ {
lv_obj_clean(lv_scr_act()); lv_obj_clean(lv_scr_act());
@@ -178,67 +210,194 @@ void show_ip_entry()
lv_obj_t * root = lv_create_empty_panel(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_obj_set_size(root, CYD_SCREEN_WIDTH_PX, CYD_SCREEN_HEIGHT_PX);
lv_layout_flex_column(root); 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_t * top_root = lv_create_empty_panel(root);
lv_obj_set_width(top_root, CYD_SCREEN_WIDTH_PX); lv_obj_set_width(top_root, CYD_SCREEN_WIDTH_PX);
lv_layout_flex_column(top_root); lv_layout_flex_column(top_root);
lv_obj_set_flex_grow(top_root, 1); lv_obj_set_flex_grow(top_root, 1);
lv_obj_set_style_pad_all(top_root, CYD_SCREEN_GAP_PX, 0); 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_obj_t * button_row = lv_create_empty_panel(top_root);
lv_label_set_text(label, "Enter Klipper IP/Hostname and Port"); lv_obj_set_size(button_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2, LV_SIZE_CONTENT);
lv_obj_set_width(label, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2); lv_layout_flex_row(button_row);
lv_obj_t * textbow_row = lv_create_empty_panel(top_root); lv_obj_t * button_back = lv_btn_create(button_row);
lv_obj_set_width(textbow_row, CYD_SCREEN_WIDTH_PX - CYD_SCREEN_GAP_PX * 2); lv_obj_set_height(button_back, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX / 2);
lv_obj_set_flex_grow(textbow_row, 1); lv_obj_set_flex_grow(button_back, 1);
lv_layout_flex_row(textbow_row); lv_obj_add_event_cb(button_back, return_to_choose_printer_type, LV_EVENT_CLICKED, NULL);
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);
portEntry = lv_textarea_create(textbow_row); lv_obj_t * label = lv_label_create(button_back);
lv_textarea_set_one_line(portEntry, true); lv_label_set_text(label, LV_SYMBOL_LEFT);
lv_textarea_set_max_length(portEntry, 5); lv_obj_center(label);
lv_obj_set_flex_grow(portEntry, 1); main_label = lv_label_create(button_row);
if (global_config.printer_config[global_config.printer_index].ip_configured)
{
char buff[7] = {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);
}
else
{
lv_textarea_set_text(hostEntry, "");
lv_textarea_set_text(portEntry, "80");
}
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_mode(keyboard, LV_KEYBOARD_MODE_USER_1);
lv_keyboard_set_textarea(keyboard, hostEntry);
if (global_config.multi_printer_mode) if (global_config.multi_printer_mode)
{ {
lv_obj_t * btn = draw_switch_printer_button(); lv_obj_t * button_switch_printer = lv_btn_create(button_row);
lv_obj_set_parent(btn, textbow_row); lv_obj_set_height(button_switch_printer, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX / 2);
lv_obj_align(btn, LV_ALIGN_DEFAULT, 0, 0); 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);
label = lv_label_create(button_switch_printer);
lv_label_set_text(label, LV_SYMBOL_HOME);
lv_obj_center(label);
} }
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 * 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);
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);
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[10] = {0};
sprintf(buff, "%d", global_config.printer_config[global_config.printer_index].klipper_port);
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(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_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, host_entry);
switch (global_config.printer_config[global_config.printer_index].printer_type)
{
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(){ void ip_init(){
if (!global_config.printer_config[global_config.printer_index].setup_complete) if (!global_config.printer_config[global_config.printer_index].setup_complete)
{
if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeNone)
{
choose_printer_type();
}
else
{ {
show_ip_entry(); show_ip_entry();
} }
}
while (!global_config.printer_config[global_config.printer_index].setup_complete) while (!global_config.printer_config[global_config.printer_index].setup_complete)
{ {

View File

@@ -8,7 +8,6 @@
#include "ui_utils.h" #include "ui_utils.h"
#include "panels/panel.h" #include "panels/panel.h"
#include "../core/lv_setup.h" #include "../core/lv_setup.h"
#include "switch_printer.h"
#include "macros.h" #include "macros.h"
void check_if_screen_needs_to_be_disabled(){ 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) 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(){ void main_ui_setup(){

View File

@@ -11,11 +11,34 @@ static void btn_click_firmware_restart(lv_event_t * e){
get_current_printer()->execute_feature(PrinterFeatureFirmwareRestart); 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) { static void set_state_message_text(lv_event_t * e) {
lv_obj_t * label = lv_event_get_target(e); lv_obj_t * label = lv_event_get_target(e);
lv_label_set_text(label, get_current_printer_data()->state_message); 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) void error_panel_init(lv_obj_t* panel)
{ {
lv_layout_flex_column(panel, LV_FLEX_ALIGN_SPACE_BETWEEN); 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_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_layout_flex_row(button_row);
lv_obj_t * btn = lv_btn_create(button_row); if (get_current_printer_data()->error_screen_features & PrinterFeatureRestart)
lv_obj_set_height(btn, CYD_SCREEN_MIN_BUTTON_HEIGHT_PX); {
lv_obj_add_event_cb(btn, btn_click_restart, LV_EVENT_CLICKED, NULL); create_button("Restart", btn_click_restart, button_row);
lv_obj_set_flex_grow(btn, 1); }
label = lv_label_create(btn); if (get_current_printer_data()->error_screen_features & PrinterFeatureFirmwareRestart)
lv_label_set_text(label, "Restart"); {
lv_obj_center(label); 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); if (get_current_printer_data()->error_screen_features & PrinterFeatureIgnoreError)
lv_obj_add_event_cb(btn, btn_click_firmware_restart, LV_EVENT_CLICKED, NULL); {
lv_obj_set_flex_grow(btn, 1); create_button("Ignore", btn_click_error_ignore, button_row);
}
label = lv_label_create(btn);
lv_label_set_text(label, "FW Restart"); if (get_current_printer_data()->error_screen_features & PrinterFeatureContinueError)
lv_obj_center(label); {
create_button("Continue", btn_click_error_continue, button_row);
}
if (get_current_printer_data()->error_screen_features & PrinterFeatureRetryError)
{
create_button("Retry", btn_click_error_retry, button_row);
}
} }

View File

@@ -5,7 +5,6 @@
#include "../../core/lv_setup.h" #include "../../core/lv_setup.h"
#include <stdio.h> #include <stdio.h>
#include "../nav_buttons.h" #include "../nav_buttons.h"
#include "../switch_printer.h"
#include "../macros.h" #include "../macros.h"
const char * printer_status[] = { const char * printer_status[] = {

View File

@@ -3,7 +3,6 @@
#include <Esp.h> #include <Esp.h>
#include <cstring> #include <cstring>
#include "../../conf/global_config.h" #include "../../conf/global_config.h"
#include "../switch_printer.h"
#include "../../core/printer_integration.hpp" #include "../../core/printer_integration.hpp"
namespace serial_console { namespace serial_console {

View File

@@ -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;
}

View File

@@ -1,5 +0,0 @@
#pragma once
#include "lvgl.h"
void switch_printer_init();
lv_obj_t * draw_switch_printer_button();

View File

@@ -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_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); 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) lv_obj_t * lv_label_btn_create(lv_obj_t * parent, lv_event_cb_t btn_callback, void* user_data)