Skip to content

Commit

Permalink
Klipper connection over serial
Browse files Browse the repository at this point in the history
  • Loading branch information
suchmememanyskill committed Nov 8, 2024
1 parent 0e57aed commit d4ce7a7
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 28 deletions.
1 change: 1 addition & 0 deletions CYD-Klipper/src/core/bambu/bambu_printer_parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
31 changes: 16 additions & 15 deletions CYD-Klipper/src/core/klipper/klipper_printer_integration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down
20 changes: 8 additions & 12 deletions CYD-Klipper/src/core/klipper/klipper_printer_parsers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,27 +183,25 @@ 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"))
{
const char *state = status["webhooks"]["state"];

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"))
Expand All @@ -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)
Expand Down

0 comments on commit d4ce7a7

Please sign in to comment.