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);
|
||||
}
|
||||
|
||||
// TODO: This isn't a 'pure' parser implementation. Remove network calls, only do parsing
|
||||
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()));
|
||||
|
||||
@@ -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;
|
||||
deserializeJson(doc, client.getStream());
|
||||
return parse_state_min(doc);
|
||||
parse_state_min(doc, &data);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -13,20 +13,21 @@ typedef struct {
|
||||
class KlipperPrinter : public BasePrinter
|
||||
{
|
||||
private:
|
||||
unsigned char lock_absolute_relative_mode_swap{};
|
||||
unsigned char klipper_request_consecutive_fail_count{};
|
||||
unsigned int slicer_estimated_print_time_s{};
|
||||
unsigned int last_slicer_time_query{};
|
||||
void configure_http_client(HTTPClient &client, String url_part, bool stream, int timeout);
|
||||
|
||||
protected:
|
||||
unsigned char lock_absolute_relative_mode_swap{};
|
||||
unsigned char klipper_request_consecutive_fail_count{};
|
||||
|
||||
bool send_emergency_stop();
|
||||
int get_slicer_time_estimate_s();
|
||||
void init_ui_panels();
|
||||
|
||||
int parse_slicer_time_estimate(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);
|
||||
int parse_macros_count(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 execute_feature(PrinterFeatures feature);
|
||||
bool connect();
|
||||
bool fetch();
|
||||
PrinterDataMinimal fetch_min();
|
||||
virtual bool connect();
|
||||
virtual bool fetch();
|
||||
virtual PrinterDataMinimal fetch_min();
|
||||
void disconnect();
|
||||
Macros get_macros();
|
||||
int get_macros_count();
|
||||
virtual Macros get_macros();
|
||||
virtual 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);
|
||||
virtual PowerDevices get_power_devices();
|
||||
virtual int get_power_devices_count();
|
||||
virtual bool set_power_device_state(const char* device_name, bool state);
|
||||
virtual Files get_files();
|
||||
virtual bool start_file(const char* filename);
|
||||
virtual 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);
|
||||
virtual bool send_gcode(const char* gcode, bool wait = true);
|
||||
};
|
||||
|
||||
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"];
|
||||
PrinterDataMinimal data = {};
|
||||
data.success = true;
|
||||
|
||||
if (status.containsKey("webhooks"))
|
||||
{
|
||||
@@ -195,15 +193,15 @@ PrinterDataMinimal KlipperPrinter::parse_state_min(JsonDocument &in)
|
||||
|
||||
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"))
|
||||
{
|
||||
data.print_progress = status["virtual_sdcard"]["progress"];
|
||||
data->print_progress = status["virtual_sdcard"]["progress"];
|
||||
}
|
||||
|
||||
if (status.containsKey("print_stats"))
|
||||
@@ -212,24 +210,22 @@ PrinterDataMinimal KlipperPrinter::parse_state_min(JsonDocument &in)
|
||||
|
||||
if (state == nullptr)
|
||||
{
|
||||
data.state = PrinterState::PrinterStateError;
|
||||
data->state = PrinterState::PrinterStateError;
|
||||
}
|
||||
else if (strcmp(state, "printing") == 0)
|
||||
{
|
||||
data.state = PrinterState::PrinterStatePrinting;
|
||||
data->state = PrinterState::PrinterStatePrinting;
|
||||
}
|
||||
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)
|
||||
{
|
||||
data.state = PrinterState::PrinterStateIdle;
|
||||
data->state = PrinterState::PrinterStateIdle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
Macros KlipperPrinter::parse_macros(JsonDocument &in)
|
||||
|
||||
Reference in New Issue
Block a user