ESP3D printer connection for wireless printing (#5399)

* ESP3D connector WIP

* Update PrintConfig.cpp

* Update ESP3D.cpp

* Update ESP3D.cpp

* prog

* works & ugly

* Update ESP3D.cpp

* ESP3D prints

* comments

* this fails on *nix, use format_error instead

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
PreyK 2024-05-22 12:00:48 +02:00 committed by GitHub
parent 9ffe7b07e1
commit b021c615f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 234 additions and 1 deletions

View file

@ -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 },
@ -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");
@ -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");

View file

@ -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 {

View file

@ -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

183
src/slic3r/Utils/ESP3D.cpp Normal file
View file

@ -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

View file

@ -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

View file

@ -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"
@ -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);