Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP3D printer connection for wireless printing #5399

Merged
merged 14 commits into from
May 22, 2024
3 changes: 3 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ static t_config_enum_values s_keys_map_PrintHostType {
{ "astrobox", htAstroBox },
{ "repetier", htRepetier },
{ "mks", htMKS },
{ "esp3d", htESP3D },
{ "obico", htObico },
{ "flashforge", htFlashforge },
{ "simplyprint", htSimplyPrint },
Expand Down Expand Up @@ -3148,6 +3149,7 @@ def = this->add("filament_loading_speed", coFloats);
def->enum_values.push_back("astrobox");
def->enum_values.push_back("repetier");
def->enum_values.push_back("mks");
def->enum_values.push_back("esp3d");
def->enum_values.push_back("obico");
def->enum_values.push_back("flashforge");
def->enum_values.push_back("simplyprint");
Expand All @@ -3159,6 +3161,7 @@ def = this->add("filament_loading_speed", coFloats);
def->enum_labels.push_back("AstroBox");
def->enum_labels.push_back("Repetier");
def->enum_labels.push_back("MKS");
def->enum_labels.push_back("ESP3D");
def->enum_labels.push_back("Obico");
def->enum_labels.push_back("Flashforge");
def->enum_labels.push_back("SimplyPrint");
Expand Down
2 changes: 1 addition & 1 deletion src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ enum class FuzzySkinType {
};

enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htObico, htFlashforge, htSimplyPrint
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htObico, htFlashforge, htSimplyPrint
};

enum AuthorizationType {
Expand Down
2 changes: 2 additions & 0 deletions src/slic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ set(SLIC3R_GUI_SOURCES
Utils/SerialMessage.hpp
Utils/MKS.cpp
Utils/MKS.hpp
Utils/ESP3D.cpp
Utils/ESP3D.hpp
Utils/WxFontUtils.cpp
Utils/WxFontUtils.hpp
Utils/Duet.cpp
Expand Down
183 changes: 183 additions & 0 deletions src/slic3r/Utils/ESP3D.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#include "ESP3D.hpp"

#include <algorithm>
#include <ctime>
#include <chrono>
#include <thread>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>

#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>

#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "Http.hpp"
#include "SerialMessage.hpp"
#include "SerialMessageType.hpp"

namespace fs = boost::filesystem;
namespace pt = boost::property_tree;

namespace Slic3r {

ESP3D::ESP3D(DynamicPrintConfig* config) : m_host(config->opt_string("print_host")), m_console_port("8888") {}

const char* ESP3D::get_name() const { return "ESP3D"; }

bool ESP3D::test(wxString& msg) const
{
bool ret = false;
std::string url_test = format_command("/command", "plain", "M105");
auto http = Http::get(url_test);
http.on_complete([&](std::string body, unsigned status) {
// check for OK
ret = true;
msg = get_test_ok_msg();
})
.on_error([&](std::string body, std::string error, unsigned status) {
ret = false;
msg = format_error(body , error, status);
})
.perform_sync();
return ret;
}

wxString ESP3D::get_test_ok_msg() const { return _(L("Connection to ESP3D works correctly.")); }

wxString ESP3D::get_test_failed_msg(wxString& msg) const
{
return GUI::from_u8((boost::format("%s: %s") % _utf8(L("Could not connect to ESP3D")) % std::string(msg.ToUTF8())).str());
}

bool ESP3D::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
{
std::string short_name = get_short_name(upload_data.upload_path.string());
bool res = false;

auto http = Http::post(std::move((boost::format("http://%1%/upload_serial") % m_host).str()));
http.header("Connection", "keep-alive")
.form_add_file("file", upload_data.source_path, short_name)
.on_complete([&](std::string body, unsigned status) {
// check for OK
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
wxString errormsg;
res = start_print(errormsg, short_name);
if (!res) {
error_fn(std::move(errormsg));
}
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("ESP3D: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool& cancel) {
// workaround:
// progress bar disappears before .on_complete
// ESP3D can be super slow, the user could close slicer before upload completes & M24 is sent because no progress bar
// M24 can only be sent after .on_complete
Http::Progress prog = std::move(progress);
prog.ulnow -= 1;
prorgess_fn(std::move(prog), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "ESP3D: Upload canceled";
res = false;
}
})
.perform_sync();

return res;
}

bool ESP3D::start_print(wxString& msg, const std::string& filename) const
{
// For some reason printer firmware does not want to respond on gcode commands immediately after file upload.
// So we just introduce artificial delay to workaround it.
// ESP3D also locks the serial during SD transfer, this is safer
std::this_thread::sleep_for(std::chrono::milliseconds(1500));

bool ret = false;
auto select_file = (boost::format("%1% %2%") % "M23" % filename).str();
auto select = format_command("/command", "plain", Http::url_encode(select_file));
auto http_sel = Http::get(select);
http_sel
.on_complete([&](std::string body, unsigned status) {
ret = true;
})
.on_error([&](std::string body, std::string error, unsigned status) {
// error sending M23
ret = false;
msg = (wxString::FromUTF8(error));
})
.perform_sync();

if (!ret)
return ret;

auto start = format_command("/command", "plain", "M24");
auto http_start = Http::get(start);
http_start
.on_complete([&](std::string body, unsigned status) {
// print kicked off succesfully
ret = true;
})
.on_error([&](std::string body, std::string error, unsigned status) {
// error sending M24
ret = false;
msg = (wxString::FromUTF8(error));
})
.perform_sync();

return ret;
}

int ESP3D::get_err_code_from_body(const std::string& body) const
{
pt::ptree root;
std::istringstream iss(body); // wrap returned json to istringstream
pt::read_json(iss, root);

return root.get<int>("err", 0);
}

// ESP3D only accepts 8.3 filenames else it crashes marlin and other undefined behaviour
std::string ESP3D::get_short_name(const std::string& filename) const
{
std::string shortname = "";
boost::filesystem::path p(filename);
std::string stem = p.stem().string();
std::string extension = p.extension().string();
if (!extension.empty() && extension[0] == '.') {
extension = extension.substr(1);
}
stem = stem.substr(0, 8);
extension = extension.substr(0, 3);
if (!extension.empty()) {
shortname = stem + "." + extension;
} else {
shortname = stem;
}
return shortname;
}

std::string ESP3D::format_command(const std::string& path, const std::string& arg, const std::string& val) const
{
return (boost::format("http://%1%%2%?%3%=%4%") % m_host % path % arg % val).str();
}

} // namespace Slic3r
43 changes: 43 additions & 0 deletions src/slic3r/Utils/ESP3D.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef slic3r_ESP3D_hpp_
#define slic3r_ESP3D_hpp_

#include <string>
#include <wx/string.h>

#include "PrintHost.hpp"
#include "TCPConsole.hpp"

namespace Slic3r {
class DynamicPrintConfig;
class Http;

class ESP3D : public PrintHost
{
public:
explicit ESP3D(DynamicPrintConfig* config);
~ESP3D() override = default;

const char* get_name() const override;

bool test(wxString& curl_msg) const override;
wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString& msg) const override;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
PrintHostPostUploadActions get_post_upload_actions() const override { return PrintHostPostUploadAction::StartPrint; }
std::string get_host() const override { return m_host; }

private:
std::string m_host;
std::string m_console_port;

bool start_print(wxString& msg, const std::string& filename) const;
int get_err_code_from_body(const std::string& body) const;
std::string get_short_name(const std::string& filename) const;
std::string format_command(const std::string& path, const std::string& arg, const std::string& val) const;
};

} // namespace Slic3r

#endif
2 changes: 2 additions & 0 deletions src/slic3r/Utils/PrintHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "AstroBox.hpp"
#include "Repetier.hpp"
#include "MKS.hpp"
#include "ESP3D.hpp"
#include "../GUI/PrintHostDialogs.hpp"
#include "Obico.hpp"
#include "Flashforge.hpp"
Expand Down Expand Up @@ -57,6 +58,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
case htPrusaLink: return new PrusaLink(config);
case htPrusaConnect: return new PrusaConnect(config);
case htMKS: return new MKS(config);
case htESP3D: return new ESP3D(config);
case htObico: return new Obico(config);
case htFlashforge: return new Flashforge(config);
case htSimplyPrint: return new SimplyPrint(config);
Expand Down
Loading