mirror of
https://github.com/suchmememanyskill/CYD-Klipper.git
synced 2026-03-21 05:33:24 +00:00
More serial
This commit is contained in:
@@ -13,6 +13,7 @@ platform = espressif32@6.4.0
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
debug_build_flags = -Os
|
||||
lib_deps =
|
||||
https://github.com/suchmememanyskill/esp32-smartdisplay#9c1d737
|
||||
bblanchon/ArduinoJson@^7.0.0
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <UrlEncode.h>
|
||||
#include "printer_integration.hpp"
|
||||
#include "klipper/klipper_printer_integration.hpp"
|
||||
#include "klipper-serial/serial_klipper_printer_integration.hpp"
|
||||
#include "bambu/bambu_printer_integration.hpp"
|
||||
|
||||
SemaphoreHandle_t freezeRenderThreadSemaphore, freezeRequestThreadSemaphore;
|
||||
@@ -122,6 +123,9 @@ void data_setup()
|
||||
case PrinterType::PrinterTypeBambuLocal:
|
||||
available_printers[count++] = new BambuPrinter(i);
|
||||
break;
|
||||
case PrinterType::PrinterTypeKlipperSerial:
|
||||
available_printers[count++] = new SerialKlipperPrinter(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
#include "serial_klipper_printer_integration.hpp"
|
||||
#include <HardwareSerial.h>
|
||||
#include <UrlEncode.h>
|
||||
#include "../../ui/serial/serial_console.h"
|
||||
|
||||
bool is_semaphores_initialized = false;
|
||||
SemaphoreHandle_t freeze_serial_thread_semaphore;
|
||||
|
||||
void semaphore_init_serial()
|
||||
{
|
||||
if (is_semaphores_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
freeze_serial_thread_semaphore = xSemaphoreCreateMutex();
|
||||
xSemaphoreGive(freeze_serial_thread_semaphore);
|
||||
is_semaphores_initialized = true;
|
||||
}
|
||||
|
||||
void freeze_serial_thread()
|
||||
{
|
||||
xSemaphoreTake(freeze_serial_thread_semaphore, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void unfreeze_serial_thread()
|
||||
{
|
||||
xSemaphoreGive(freeze_serial_thread_semaphore);
|
||||
}
|
||||
|
||||
enum HttpRequestType
|
||||
{
|
||||
@@ -18,35 +44,37 @@ void clear_serial_buffer()
|
||||
|
||||
// Request: {timeout} {method} {endpoint}
|
||||
// Response: {status code} {body}
|
||||
int make_serial_request(JsonDocument &out, int timeout_ms, HttpRequestType requestType, const char* endpoint)
|
||||
bool make_serial_request(JsonDocument &out, int timeout_ms, HttpRequestType requestType, const char* endpoint)
|
||||
{
|
||||
semaphore_init_serial();
|
||||
freeze_serial_thread();
|
||||
serial_console::global_disable_serial_console = true;
|
||||
temporary_config.debug = false;
|
||||
char buff[10];
|
||||
clear_serial_buffer();
|
||||
|
||||
// TODO: Add semaphore here
|
||||
if (!Serial.availableForWrite())
|
||||
{
|
||||
return -1;
|
||||
unfreeze_serial_thread();
|
||||
return false;
|
||||
}
|
||||
|
||||
char buff[10];
|
||||
sprintf(buff, "%d ", timeout_ms);
|
||||
|
||||
// TODO: Maybe use printf?
|
||||
Serial.write(buff);
|
||||
Serial.write(requestType == HttpGet ? "GET" : "POST");
|
||||
Serial.write(' ');
|
||||
Serial.write(endpoint);
|
||||
Serial.write('\n');
|
||||
Serial.printf("HTTP_REQUEST %d %s %s\n", timeout_ms, requestType == HttpGet ? "GET" : "POST", endpoint);
|
||||
|
||||
if (timeout_ms <= 0)
|
||||
{
|
||||
return 200;
|
||||
unfreeze_serial_thread();
|
||||
return true;
|
||||
}
|
||||
unsigned long _m = millis();
|
||||
while (!Serial.available() && millis() < _m + timeout_ms + 10) delay(1);
|
||||
|
||||
if (!Serial.available())
|
||||
{
|
||||
return -2;
|
||||
Serial.println("Timeout...");
|
||||
unfreeze_serial_thread();
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.readBytes(buff, 4);
|
||||
@@ -54,26 +82,39 @@ int make_serial_request(JsonDocument &out, int timeout_ms, HttpRequestType reque
|
||||
|
||||
if (buff[0] < '0' || buff[0] > '9')
|
||||
{
|
||||
Serial.println("Invalid error code");
|
||||
clear_serial_buffer();
|
||||
return -3;
|
||||
unfreeze_serial_thread();
|
||||
return false;
|
||||
}
|
||||
|
||||
int status_code = atoi(buff);
|
||||
|
||||
if (status_code < 200 || status_code >= 300)
|
||||
{
|
||||
Serial.println("Non-200 error code");
|
||||
clear_serial_buffer();
|
||||
return -4;
|
||||
unfreeze_serial_thread();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto result = deserializeJson(out, Serial);
|
||||
return result == DeserializationError::Ok;
|
||||
Serial.printf("Deserialization result: %s\n", result.c_str());
|
||||
bool success = result == DeserializationError::Ok;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
unfreeze_serial_thread();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool make_serial_request_nocontent(HttpRequestType requestType, const char* endpoint)
|
||||
{
|
||||
JsonDocument doc;
|
||||
make_serial_request(doc, 0, requestType, endpoint);
|
||||
unfreeze_serial_thread();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -85,7 +126,7 @@ bool SerialKlipperPrinter::connect()
|
||||
bool SerialKlipperPrinter::fetch()
|
||||
{
|
||||
JsonDocument doc;
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks&fan&display_status") == 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?extruder&heater_bed&toolhead&gcode_move&virtual_sdcard&print_stats&webhooks&fan&display_status"))
|
||||
{
|
||||
if (printer_data.state == PrinterStateOffline)
|
||||
{
|
||||
@@ -94,6 +135,7 @@ bool SerialKlipperPrinter::fetch()
|
||||
|
||||
klipper_request_consecutive_fail_count = 0;
|
||||
parse_state(doc);
|
||||
unfreeze_serial_thread();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -122,11 +164,11 @@ PrinterDataMinimal SerialKlipperPrinter::fetch_min()
|
||||
|
||||
data.success = true;
|
||||
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?webhooks&print_stats&virtual_sdcard") == 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?webhooks&print_stats&virtual_sdcard"))
|
||||
{
|
||||
data.state = PrinterState::PrinterStateIdle;
|
||||
parse_state_min(doc, &data);
|
||||
doc.clear();
|
||||
unfreeze_serial_thread();
|
||||
data.power_devices = get_power_devices_count();
|
||||
}
|
||||
else
|
||||
@@ -142,9 +184,10 @@ Macros SerialKlipperPrinter::get_macros()
|
||||
{
|
||||
Macros macros = {0};
|
||||
JsonDocument doc;
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help") == 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help"))
|
||||
{
|
||||
return parse_macros(doc);
|
||||
macros = parse_macros(doc);
|
||||
unfreeze_serial_thread();
|
||||
}
|
||||
|
||||
return macros;
|
||||
@@ -153,9 +196,11 @@ Macros SerialKlipperPrinter::get_macros()
|
||||
int SerialKlipperPrinter::get_macros_count()
|
||||
{
|
||||
JsonDocument doc;
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help") == 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help"))
|
||||
{
|
||||
return parse_macros_count(doc);
|
||||
int count = parse_macros_count(doc);
|
||||
unfreeze_serial_thread();
|
||||
return count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -165,9 +210,10 @@ PowerDevices SerialKlipperPrinter::get_power_devices()
|
||||
{
|
||||
PowerDevices power_devices = {0};
|
||||
JsonDocument doc;
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices") == 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices"))
|
||||
{
|
||||
return parse_power_devices(doc);
|
||||
power_devices = parse_power_devices(doc);
|
||||
unfreeze_serial_thread();
|
||||
}
|
||||
|
||||
return power_devices;
|
||||
@@ -176,9 +222,11 @@ PowerDevices SerialKlipperPrinter::get_power_devices()
|
||||
int SerialKlipperPrinter::get_power_devices_count()
|
||||
{
|
||||
JsonDocument doc;
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices") == 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices"))
|
||||
{
|
||||
return parse_power_devices_count(doc);
|
||||
int count = parse_power_devices_count(doc);
|
||||
unfreeze_serial_thread();
|
||||
return count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -186,24 +234,58 @@ int SerialKlipperPrinter::get_power_devices_count()
|
||||
|
||||
bool SerialKlipperPrinter::set_power_device_state(const char* device_name, bool state)
|
||||
{
|
||||
JsonDocument doc;
|
||||
String request = "/machine/device_power/device?device=" + urlEncode(device_name) + "&action=" + (state ? "on" : "off");
|
||||
return make_serial_request(doc, 1000, HttpPost, request.c_str());
|
||||
return make_serial_request_nocontent(HttpGet, request.c_str());
|
||||
}
|
||||
|
||||
Files SerialKlipperPrinter::get_files()
|
||||
{
|
||||
// TODO: Stubbed
|
||||
Files files = {0};
|
||||
files.success = false;
|
||||
return files;
|
||||
Files files_result = {0};
|
||||
files_result.success = false;
|
||||
JsonDocument doc;
|
||||
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
|
||||
std::list<FileSystemFile> files;
|
||||
|
||||
auto timer_request = millis();
|
||||
bool result = make_serial_request(doc, 5000, HttpGet, "/server/files/list");
|
||||
auto timer_parse = millis();
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return files_result;
|
||||
}
|
||||
|
||||
parse_file_list(doc, files, 20);
|
||||
unfreeze_serial_thread();
|
||||
|
||||
files_result.available_files = (char**)malloc(sizeof(char*) * files.size());
|
||||
|
||||
if (files_result.available_files == NULL){
|
||||
LOG_LN("Failed to allocate memory");
|
||||
|
||||
for (auto file : files){
|
||||
free(file.name);
|
||||
}
|
||||
|
||||
return files_result;
|
||||
}
|
||||
|
||||
for (auto file : files){
|
||||
files_result.available_files[files_result.count++] = file.name;
|
||||
}
|
||||
|
||||
files_result.success = true;
|
||||
|
||||
LOG_F(("Heap space post-file-parse: %d bytes\n", esp_get_free_heap_size()))
|
||||
LOG_F(("Got %d files. Request took %dms, parsing took %dms\n", files.size(), timer_parse - timer_request, millis() - timer_parse))
|
||||
return files_result;
|
||||
}
|
||||
|
||||
bool SerialKlipperPrinter::start_file(const char* filename)
|
||||
{
|
||||
JsonDocument doc;
|
||||
String request = "/printer/print/start?filename=" + urlEncode(filename);
|
||||
return make_serial_request(doc, 1000, HttpPost, request.c_str());
|
||||
return make_serial_request_nocontent(HttpGet, request.c_str());;
|
||||
}
|
||||
|
||||
Thumbnail SerialKlipperPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename)
|
||||
@@ -218,16 +300,25 @@ bool SerialKlipperPrinter::send_gcode(const char* gcode, bool wait)
|
||||
{
|
||||
JsonDocument doc;
|
||||
String request = "/printer/gcode/script?script=" + urlEncode(gcode);
|
||||
return wait
|
||||
? make_serial_request(doc, 5000, HttpGet, request.c_str())
|
||||
: make_serial_request_nocontent(HttpGet, request.c_str());
|
||||
|
||||
if (!wait)
|
||||
{
|
||||
return make_serial_request_nocontent(HttpGet, request.c_str());
|
||||
}
|
||||
|
||||
bool result = make_serial_request(doc, 5000, HttpGet, request.c_str());
|
||||
unfreeze_serial_thread();
|
||||
return result;
|
||||
}
|
||||
|
||||
KlipperConnectionStatus connection_test_serial_klipper(PrinterConfiguration* config)
|
||||
{
|
||||
serial_console::global_disable_serial_console = true;
|
||||
temporary_config.debug = false;
|
||||
JsonDocument doc;
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/info") != 200)
|
||||
if (make_serial_request(doc, 1000, HttpGet, "/printer/info"))
|
||||
{
|
||||
unfreeze_serial_thread();
|
||||
return KlipperConnectionStatus::ConnectOk;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ class SerialKlipperPrinter : public KlipperPrinter
|
||||
{
|
||||
public:
|
||||
SerialKlipperPrinter(int index) : KlipperPrinter(index)
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
bool connect();
|
||||
bool fetch();
|
||||
@@ -20,4 +18,6 @@ class SerialKlipperPrinter : public KlipperPrinter
|
||||
bool start_file(const char* filename);
|
||||
Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
|
||||
bool send_gcode(const char* gcode, bool wait = true);
|
||||
};
|
||||
};
|
||||
|
||||
KlipperConnectionStatus connection_test_serial_klipper(PrinterConfiguration* config);
|
||||
@@ -18,16 +18,11 @@ void setup() {
|
||||
screen_setup();
|
||||
lv_setup();
|
||||
LOG_LN("Screen init done");
|
||||
LOG_F(("Free heap: %d bytes\n", esp_get_free_heap_size()));
|
||||
|
||||
wifi_init();
|
||||
LOG_F(("Free heap after wifi setup: %d bytes\n", esp_get_free_heap_size()));
|
||||
ota_init();
|
||||
LOG_F(("Free heap after ota setup: %d bytes\n", esp_get_free_heap_size()));
|
||||
ip_init();
|
||||
LOG_F(("Free heap after ip setup: %d bytes\n", esp_get_free_heap_size()));
|
||||
data_setup();
|
||||
LOG_F(("Free heap after data setup: %d bytes\n", esp_get_free_heap_size()));
|
||||
|
||||
nav_style_setup();
|
||||
main_ui_setup();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "../core/klipper/klipper_printer_integration.hpp"
|
||||
#include "../core/bambu/bambu_printer_integration.hpp"
|
||||
#include "../core/screen_driver.h"
|
||||
#include "../core/klipper-serial/serial_klipper_printer_integration.hpp"
|
||||
|
||||
void show_ip_entry();
|
||||
void choose_printer_type();
|
||||
@@ -57,6 +58,26 @@ static void btn_switch_printer(lv_event_t *e){
|
||||
lv_obj_del(lv_obj_get_parent(lv_obj_get_parent(btn)));
|
||||
}
|
||||
|
||||
long last_request = 0;
|
||||
|
||||
void serial_check_connection()
|
||||
{
|
||||
if ((millis() - last_request) < 5000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = connection_test_serial_klipper(&global_config.printer_config[global_config.printer_index]);
|
||||
last_request = millis();
|
||||
|
||||
if (result == KlipperConnectionStatus::ConnectOk)
|
||||
{
|
||||
global_config.printer_config[global_config.printer_index].setup_complete = true;
|
||||
strcpy(global_config.printer_config[global_config.printer_index].klipper_host, "Serial");
|
||||
write_global_config();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -338,6 +359,20 @@ void show_ip_entry()
|
||||
lv_textarea_set_placeholder_text(port_entry, "Access code");
|
||||
lv_textarea_set_placeholder_text(auth_entry, "Printer serial number");
|
||||
break;
|
||||
case PrinterType::PrinterTypeKlipperSerial:
|
||||
lv_label_set_text(main_label, "Klipper (Serial) Setup");
|
||||
lv_obj_del(ip_row);
|
||||
lv_obj_del(auth_row);
|
||||
lv_obj_del(keyboard);
|
||||
|
||||
lv_obj_t * bottom_root = lv_create_empty_panel(top_root);
|
||||
lv_obj_set_width(bottom_root, CYD_SCREEN_WIDTH_PX);
|
||||
lv_obj_set_flex_grow(bottom_root, 1);
|
||||
|
||||
label = lv_label_create(bottom_root);
|
||||
lv_obj_center(label);
|
||||
lv_label_set_text(label, "Connect CYD-Klipper to a host\nrunning the CYD-Klipper server");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +388,12 @@ static void printer_type_bambu_local(lv_event_t * e)
|
||||
show_ip_entry();
|
||||
}
|
||||
|
||||
static void printer_type_serial_klipper(lv_event_t * e)
|
||||
{
|
||||
global_config.printer_config[global_config.printer_index].printer_type = PrinterType::PrinterTypeKlipperSerial;
|
||||
show_ip_entry();
|
||||
}
|
||||
|
||||
void choose_printer_type()
|
||||
{
|
||||
lv_obj_clean(lv_scr_act());
|
||||
@@ -377,6 +418,14 @@ void choose_printer_type()
|
||||
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_serial_klipper, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
label = lv_label_create(btn);
|
||||
lv_label_set_text(label, "Klipper (Serial)");
|
||||
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);
|
||||
@@ -401,6 +450,11 @@ void ip_init(){
|
||||
|
||||
while (!global_config.printer_config[global_config.printer_index].setup_complete)
|
||||
{
|
||||
if (global_config.printer_config[global_config.printer_index].printer_type == PrinterType::PrinterTypeKlipperSerial)
|
||||
{
|
||||
serial_check_connection();
|
||||
}
|
||||
|
||||
lv_handler();
|
||||
serial_console::run();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ static void btn_print_file_verify(lv_event_t * e){
|
||||
lv_img_dsc_t* img_header = (lv_img_dsc_t*)malloc(sizeof(lv_img_dsc_t));
|
||||
lv_obj_on_destroy_free_data(panel, img_header);
|
||||
|
||||
memset(img_header, 0, sizeof(img_header));
|
||||
memset(img_header, 0, sizeof(lv_img_dsc_t));
|
||||
img_header->header.w = 32;
|
||||
img_header->header.h = 32;
|
||||
img_header->data_size = thumbnail.size;
|
||||
|
||||
@@ -167,7 +167,9 @@ static void home_button_click(lv_event_t * e) {
|
||||
if (get_current_printer_data()->state == PrinterState::PrinterStatePrinting)
|
||||
return;
|
||||
|
||||
freeze_request_thread();
|
||||
get_current_printer()->execute_feature(PrinterFeatures::PrinterFeatureHome);
|
||||
unfreeze_request_thread();
|
||||
}
|
||||
|
||||
static void disable_steppers_click(lv_event_t * e) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
namespace serial_console
|
||||
{
|
||||
bool global_disable_serial_console = false;
|
||||
|
||||
/*
|
||||
* read_string_until: Non-blocking replacement for Serial.readStringUntil()..
|
||||
@@ -26,7 +27,7 @@ namespace serial_console
|
||||
{
|
||||
--cnt;
|
||||
|
||||
// backspace
|
||||
// backspaceF
|
||||
if (c == 8)
|
||||
{
|
||||
if (index > 0)
|
||||
@@ -131,6 +132,9 @@ namespace serial_console
|
||||
// main "engine": non-blockingly read until newline found, parse, execute.
|
||||
void run()
|
||||
{
|
||||
if (global_disable_serial_console)
|
||||
return;
|
||||
|
||||
static char cmdline[MAX_COMDLINE_SIZE + 1] = {0};
|
||||
if (!read_string_until('\n', cmdline, MAX_COMDLINE_SIZE))
|
||||
return;
|
||||
|
||||
@@ -5,4 +5,6 @@ namespace serial_console {
|
||||
void greet();
|
||||
void run();
|
||||
|
||||
extern bool global_disable_serial_console;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user