mirror of
https://github.com/suchmememanyskill/CYD-Klipper.git
synced 2026-03-21 05:33:24 +00:00
Klipper connection over serial
This commit is contained in:
@@ -301,6 +301,7 @@ bool send_command_without_response(WiFiClientSecure& client, const char* command
|
|||||||
return wifi_client_response_pass(client);
|
return wifi_client_response_pass(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This isn't a 'pure' parser implementation. Remove network calls, only do parsing
|
||||||
Files BambuPrinter::parse_files(WiFiClientSecure& wifi_client, int max_files)
|
Files BambuPrinter::parse_files(WiFiClientSecure& wifi_client, int max_files)
|
||||||
{
|
{
|
||||||
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
|
LOG_F(("Heap space pre-file-parse: %d bytes\n", esp_get_free_heap_size()));
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
#include "serial_klipper_printer_integration.hpp"
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <UrlEncode.h>
|
||||||
|
|
||||||
|
enum HttpRequestType
|
||||||
|
{
|
||||||
|
HttpPost,
|
||||||
|
HttpGet
|
||||||
|
};
|
||||||
|
|
||||||
|
void clear_serial_buffer()
|
||||||
|
{
|
||||||
|
while (Serial.available())
|
||||||
|
{
|
||||||
|
Serial.read();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request: {timeout} {method} {endpoint}
|
||||||
|
// Response: {status code} {body}
|
||||||
|
int make_serial_request(JsonDocument &out, int timeout_ms, HttpRequestType requestType, const char* endpoint)
|
||||||
|
{
|
||||||
|
clear_serial_buffer();
|
||||||
|
// TODO: Add semaphore here
|
||||||
|
if (!Serial.availableForWrite())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
|
if (timeout_ms <= 0)
|
||||||
|
{
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
unsigned long _m = millis();
|
||||||
|
while (!Serial.available() && millis() < _m + timeout_ms + 10) delay(1);
|
||||||
|
|
||||||
|
if (!Serial.available())
|
||||||
|
{
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.readBytes(buff, 4);
|
||||||
|
buff[3] = 0;
|
||||||
|
|
||||||
|
if (buff[0] < '0' || buff[0] > '9')
|
||||||
|
{
|
||||||
|
clear_serial_buffer();
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status_code = atoi(buff);
|
||||||
|
|
||||||
|
if (status_code < 200 || status_code >= 300)
|
||||||
|
{
|
||||||
|
clear_serial_buffer();
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = deserializeJson(out, Serial);
|
||||||
|
return result == DeserializationError::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool make_serial_request_nocontent(HttpRequestType requestType, const char* endpoint)
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
make_serial_request(doc, 0, requestType, endpoint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SerialKlipperPrinter::connect()
|
||||||
|
{
|
||||||
|
return connection_test_serial_klipper(printer_config) == KlipperConnectionStatus::ConnectOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (printer_data.state == PrinterStateOffline)
|
||||||
|
{
|
||||||
|
printer_data.state = PrinterStateError;
|
||||||
|
}
|
||||||
|
|
||||||
|
klipper_request_consecutive_fail_count = 0;
|
||||||
|
parse_state(doc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
klipper_request_consecutive_fail_count++;
|
||||||
|
if (klipper_request_consecutive_fail_count >= 5)
|
||||||
|
{
|
||||||
|
printer_data.state = PrinterStateOffline;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrinterDataMinimal SerialKlipperPrinter::fetch_min()
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
PrinterDataMinimal data = {};
|
||||||
|
data.success = false;
|
||||||
|
|
||||||
|
if (!printer_config->setup_complete)
|
||||||
|
{
|
||||||
|
data.state = PrinterStateOffline;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.success = true;
|
||||||
|
|
||||||
|
if (make_serial_request(doc, 1000, HttpGet, "/printer/objects/query?webhooks&print_stats&virtual_sdcard") == 200)
|
||||||
|
{
|
||||||
|
data.state = PrinterState::PrinterStateIdle;
|
||||||
|
parse_state_min(doc, &data);
|
||||||
|
doc.clear();
|
||||||
|
data.power_devices = get_power_devices_count();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.state = PrinterState::PrinterStateOffline;
|
||||||
|
data.power_devices = get_power_devices_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Macros SerialKlipperPrinter::get_macros()
|
||||||
|
{
|
||||||
|
Macros macros = {0};
|
||||||
|
JsonDocument doc;
|
||||||
|
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help") == 200)
|
||||||
|
{
|
||||||
|
return parse_macros(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return macros;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SerialKlipperPrinter::get_macros_count()
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
if (make_serial_request(doc, 1000, HttpGet, "/printer/gcode/help") == 200)
|
||||||
|
{
|
||||||
|
return parse_macros_count(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerDevices SerialKlipperPrinter::get_power_devices()
|
||||||
|
{
|
||||||
|
PowerDevices power_devices = {0};
|
||||||
|
JsonDocument doc;
|
||||||
|
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices") == 200)
|
||||||
|
{
|
||||||
|
return parse_power_devices(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return power_devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SerialKlipperPrinter::get_power_devices_count()
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
if (make_serial_request(doc, 1000, HttpGet, "/machine/device_power/devices") == 200)
|
||||||
|
{
|
||||||
|
return parse_power_devices_count(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
Files SerialKlipperPrinter::get_files()
|
||||||
|
{
|
||||||
|
// TODO: Stubbed
|
||||||
|
Files files = {0};
|
||||||
|
files.success = false;
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
Thumbnail SerialKlipperPrinter::get_32_32_png_image_thumbnail(const char* gcode_filename)
|
||||||
|
{
|
||||||
|
// TODO: Stubbed
|
||||||
|
Thumbnail thumbnail = {0};
|
||||||
|
thumbnail.success = false;
|
||||||
|
return thumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
KlipperConnectionStatus connection_test_serial_klipper(PrinterConfiguration* config)
|
||||||
|
{
|
||||||
|
JsonDocument doc;
|
||||||
|
if (make_serial_request(doc, 1000, HttpGet, "/printer/info") != 200)
|
||||||
|
{
|
||||||
|
return KlipperConnectionStatus::ConnectOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KlipperConnectionStatus::ConnectFail;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "../klipper/klipper_printer_integration.hpp"
|
||||||
|
|
||||||
|
class SerialKlipperPrinter : public KlipperPrinter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SerialKlipperPrinter(int index) : KlipperPrinter(index)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connect();
|
||||||
|
bool fetch();
|
||||||
|
PrinterDataMinimal fetch_min();
|
||||||
|
Macros get_macros();
|
||||||
|
int get_macros_count();
|
||||||
|
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 send_gcode(const char* gcode, bool wait = true);
|
||||||
|
};
|
||||||
@@ -226,7 +226,7 @@ PrinterDataMinimal KlipperPrinter::fetch_min()
|
|||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
deserializeJson(doc, client.getStream());
|
deserializeJson(doc, client.getStream());
|
||||||
return parse_state_min(doc);
|
parse_state_min(doc, &data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,20 +13,21 @@ typedef struct {
|
|||||||
class KlipperPrinter : public BasePrinter
|
class KlipperPrinter : public BasePrinter
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
unsigned char lock_absolute_relative_mode_swap{};
|
|
||||||
unsigned char klipper_request_consecutive_fail_count{};
|
|
||||||
unsigned int slicer_estimated_print_time_s{};
|
unsigned int slicer_estimated_print_time_s{};
|
||||||
unsigned int last_slicer_time_query{};
|
unsigned int last_slicer_time_query{};
|
||||||
void configure_http_client(HTTPClient &client, String url_part, bool stream, int timeout);
|
void configure_http_client(HTTPClient &client, String url_part, bool stream, int timeout);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
unsigned char lock_absolute_relative_mode_swap{};
|
||||||
|
unsigned char klipper_request_consecutive_fail_count{};
|
||||||
|
|
||||||
bool send_emergency_stop();
|
bool send_emergency_stop();
|
||||||
int get_slicer_time_estimate_s();
|
int get_slicer_time_estimate_s();
|
||||||
void init_ui_panels();
|
void init_ui_panels();
|
||||||
|
|
||||||
int parse_slicer_time_estimate(JsonDocument& in);
|
int parse_slicer_time_estimate(JsonDocument& in);
|
||||||
void parse_state(JsonDocument& in);
|
void parse_state(JsonDocument& in);
|
||||||
PrinterDataMinimal parse_state_min(JsonDocument& in);
|
void parse_state_min(JsonDocument &in, PrinterDataMinimal* data);
|
||||||
Macros parse_macros(JsonDocument &in);
|
Macros parse_macros(JsonDocument &in);
|
||||||
int parse_macros_count(JsonDocument &in);
|
int parse_macros_count(JsonDocument &in);
|
||||||
PowerDevices parse_power_devices(JsonDocument &in);
|
PowerDevices parse_power_devices(JsonDocument &in);
|
||||||
@@ -60,21 +61,21 @@ class KlipperPrinter : public BasePrinter
|
|||||||
|
|
||||||
bool move_printer(const char* axis, float amount, bool relative);
|
bool move_printer(const char* axis, float amount, bool relative);
|
||||||
bool execute_feature(PrinterFeatures feature);
|
bool execute_feature(PrinterFeatures feature);
|
||||||
bool connect();
|
virtual bool connect();
|
||||||
bool fetch();
|
virtual bool fetch();
|
||||||
PrinterDataMinimal fetch_min();
|
virtual PrinterDataMinimal fetch_min();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
Macros get_macros();
|
virtual Macros get_macros();
|
||||||
int get_macros_count();
|
virtual int get_macros_count();
|
||||||
bool execute_macro(const char* macro);
|
bool execute_macro(const char* macro);
|
||||||
PowerDevices get_power_devices();
|
virtual PowerDevices get_power_devices();
|
||||||
int get_power_devices_count();
|
virtual int get_power_devices_count();
|
||||||
bool set_power_device_state(const char* device_name, bool state);
|
virtual bool set_power_device_state(const char* device_name, bool state);
|
||||||
Files get_files();
|
virtual Files get_files();
|
||||||
bool start_file(const char* filename);
|
virtual bool start_file(const char* filename);
|
||||||
Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
|
virtual Thumbnail get_32_32_png_image_thumbnail(const char* gcode_filename);
|
||||||
bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature);
|
bool set_target_temperature(PrinterTemperatureDevice device, unsigned int temperature);
|
||||||
bool send_gcode(const char* gcode, bool wait = true);
|
virtual bool send_gcode(const char* gcode, bool wait = true);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum KlipperConnectionStatus {
|
enum KlipperConnectionStatus {
|
||||||
|
|||||||
@@ -183,11 +183,9 @@ void KlipperPrinter::parse_state(JsonDocument &in)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrinterDataMinimal KlipperPrinter::parse_state_min(JsonDocument &in)
|
void KlipperPrinter::parse_state_min(JsonDocument &in, PrinterDataMinimal* data)
|
||||||
{
|
{
|
||||||
auto status = in["result"]["status"];
|
auto status = in["result"]["status"];
|
||||||
PrinterDataMinimal data = {};
|
|
||||||
data.success = true;
|
|
||||||
|
|
||||||
if (status.containsKey("webhooks"))
|
if (status.containsKey("webhooks"))
|
||||||
{
|
{
|
||||||
@@ -195,15 +193,15 @@ PrinterDataMinimal KlipperPrinter::parse_state_min(JsonDocument &in)
|
|||||||
|
|
||||||
if (strcmp(state, "shutdown") == 0)
|
if (strcmp(state, "shutdown") == 0)
|
||||||
{
|
{
|
||||||
data.state = PrinterState::PrinterStateError;
|
data->state = PrinterState::PrinterStateError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.state != PrinterStateError)
|
if (data->state != PrinterStateError)
|
||||||
{
|
{
|
||||||
if (status.containsKey("virtual_sdcard"))
|
if (status.containsKey("virtual_sdcard"))
|
||||||
{
|
{
|
||||||
data.print_progress = status["virtual_sdcard"]["progress"];
|
data->print_progress = status["virtual_sdcard"]["progress"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.containsKey("print_stats"))
|
if (status.containsKey("print_stats"))
|
||||||
@@ -212,24 +210,22 @@ PrinterDataMinimal KlipperPrinter::parse_state_min(JsonDocument &in)
|
|||||||
|
|
||||||
if (state == nullptr)
|
if (state == nullptr)
|
||||||
{
|
{
|
||||||
data.state = PrinterState::PrinterStateError;
|
data->state = PrinterState::PrinterStateError;
|
||||||
}
|
}
|
||||||
else if (strcmp(state, "printing") == 0)
|
else if (strcmp(state, "printing") == 0)
|
||||||
{
|
{
|
||||||
data.state = PrinterState::PrinterStatePrinting;
|
data->state = PrinterState::PrinterStatePrinting;
|
||||||
}
|
}
|
||||||
else if (strcmp(state, "paused") == 0)
|
else if (strcmp(state, "paused") == 0)
|
||||||
{
|
{
|
||||||
data.state = PrinterState::PrinterStatePaused;
|
data->state = PrinterState::PrinterStatePaused;
|
||||||
}
|
}
|
||||||
else if (strcmp(state, "complete") == 0 || strcmp(state, "cancelled") == 0 || strcmp(state, "standby") == 0)
|
else if (strcmp(state, "complete") == 0 || strcmp(state, "cancelled") == 0 || strcmp(state, "standby") == 0)
|
||||||
{
|
{
|
||||||
data.state = PrinterState::PrinterStateIdle;
|
data->state = PrinterState::PrinterStateIdle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Macros KlipperPrinter::parse_macros(JsonDocument &in)
|
Macros KlipperPrinter::parse_macros(JsonDocument &in)
|
||||||
|
|||||||
Reference in New Issue
Block a user