击上方“嵌入式应用研究院”,选择“置顶/星标公众号

干货福利,第一时间送达!

建议不是本行又感兴趣的小伙伴们先看下面两篇了解一下Marlin:

开源Marlin2.x源代码架构学习笔记

3D打印机marlin固件框架与GCode命令总结

YouTube上的老外通俗易懂的方式讲解Marlin2.0

然后再看下面的文章。

1、摘要

说到 RepRaptor,我们就有必要来了解下RepRap 3D打印机。RepRap是人类历史上第一部可以自我复制型的机器。RepRap是一部可以生成塑料实物的免费桌面型 3D 打印机。由于 RepRap 很多部件都是由塑料制成了,并且 RepRap 都可以进行打印,所以 RepRap 可以自我复制。也就是说,任何人都可以花一些时间收集够材料进行制作。这也意味着,如果您已经有一台 RepRap 了,您可以在打印很多有用的物件的同时,为朋友再打印出另一部 RepRap。RepRap 致力于自我复制型机器的制作,并无偿的提供给大家使用。我们用 3D 打印来实现这些,但如果您使用其它的技术也实现了自我复制并愿意无偿提供给大家使用。那么,这里也将非常欢迎您的加入。RepRap是第一款低成本 3D 打印机,并且 RepRap 还开创了开源 3D 打印机的革命。在手工制作类社区的所有成员中被广泛使用的一款 3D 打印机。

关键词:3D打印机;自我复制;RepRap;RepRaptor

2、RepRaptor简介

RepRaptor是一个可用于支持GCode指令3D打印上位机,它是用QT5来编写的。之所以使用QT5来编写,这是因为开发者希望它能够任何硬件上运行。因此,RepRaptor也可以用于控制RepRap 来实现3D模型的打印。它的界面是这样的:

我们能够看到的是,它已经具备了3D打印机上位机的基本雏形。目前3D打印机的主流的架构主要是这样的:

因此,RepRaptor相当于充当了上位机这个部分的工作:

只可惜,这个版本仅仅发布到了RepRaptor v0.3.8以后就没有再进行继续更新了。

但是没关系,我们可以基于这个雏形,做出属于我们自己的3D打印机上位机,然后我们就可以买一台支持联机打印的3D打印机,愉快的进行模型打印了!

3、RepRaptor源码架构导读

在开发属于自己的3D打印机上位机之前,我们必须获得它的源码:

git clone https://github.com/josefprusa/RepRapCalculator.git

然后使用QT Creator将其打开,接下来我们就可以看到下面的代码结构:

3.1、界面介绍

在这里,我们可以看到这是一个基于QT Designer做UI界面,类似于MFC一样拖控件,然后再使用C++写逻辑。因此,我们能够看到它是由6个UI界面来完成的,分别是:

(1) aboutwindow.ui

关于项目的介绍

(2)eepromwindow.ui

获取打印机EEPROM中的数据并展现到界面上来,此部分功能不全。

(3) errorwindow.ui      

一些运行错误的提示窗口

(4)mainwindow.ui

主页面,用于控制打印机的常规操作、获取打印机反馈的信息,例如温度、速度:

(5)sdwindow.ui

使用SD卡进行打印锁需要的设置和文件读取等功能,此部分功能不全。

(6)settingswindow.ui

一些参数的设置,此部分功能不全

3.2、核心代码架构导读

4、打造属于我们自己的3D打印机上位机

4.1、成功打造属于我们自己的3D打印机上位机的前提

当然,想要学会打造自己的打印机的前提,你得具备以下基础知识:

  • 掌握QT软件开发(如果你会C#或者其它当然也没问题)

  • 掌握3D打印机GCode指令协议

  • 其它必要的知识,如设计模式、数据结构等。

4.2、核心交互逻辑的实现

关于GCode的格式以及响应的通讯协议可以参考:

https://marlinfw.org/meta/gcode/

这上面列出了几乎所有Marlin支持的GCode协议指令。

从源码导读部分,我们最需要关心的是mainwindow.cpp、sender.cpp和parser.cpp这三个文件,因为它们是实现3D打印机上位机成功的基础,这里我们能够看到这三个线程之间的交集部分,也就是mainwindow.cpp里的这段代码:

//Parser thread signal-slots and init
parserWorker->moveToThread(parserThread);
connect(parserThread, &QThread::finished, parserWorker, &QObject::deleteLater);
connect(this, &MainWindow::startedReadingEEPROM, parserWorker, &Parser::setEEPROMReadingMode);
connect(parserWorker, &Parser::receivedTemperature, this, &MainWindow::updateTemperature);
connect(parserWorker, &Parser::receivedSDFilesList, this, &MainWindow::initSDprinting);
connect(parserWorker, &Parser::receivedEEPROMLine, this, &MainWindow::EEPROMSettingReceived);
connect(parserWorker, &Parser::recievingEEPROMDone, this, &MainWindow::openEEPROMeditor);
connect(parserWorker, &Parser::receivedError, this, &MainWindow::receivedError);
connect(parserWorker, &Parser::receivedSDDone, this, &MainWindow::receivedSDDone);
connect(parserWorker, &Parser::receivedSDUpdate, this, &MainWindow::updateSDStatus);connect(parserWorker, &Parser::receivedNotSDPrinting, this, &MainWindow::receivedNotSDPrinting);
parserThread->start();
parserThread->setPriority(QThread::HighestPriority);//Sender thread signal-slots and init
senderWorker->moveToThread(senderThread);
connect(senderThread, &QThread::finished, senderWorker, &QObject::deleteLater);
connect(parserWorker, &Parser::receivedOkNum, senderWorker, &Sender::receivedOkNum);
connect(parserWorker, &Parser::receivedOkWait, senderWorker, &Sender::receivedOkWait);
connect(parserWorker, &Parser::receivedResend, senderWorker, &Sender::receivedResend);
connect(parserWorker, &Parser::receivedStart, senderWorker, &Sender::receivedStart);
connect(senderWorker, &Sender::errorReceived, this, &MainWindow::serialError);
connect(senderWorker, &Sender::dataReceived, parserWorker, &Parser::parse);
connect(senderWorker, &Sender::dataReceived, this, &MainWindow::readSerial);
connect(senderWorker, &Sender::reportProgress, this, &MainWindow::updateFileProgress);
connect(senderWorker, &Sender::baudrateSetFailed, this, &MainWindow::baudrateSetFailed);
connect(this, &MainWindow::setFile, senderWorker, &Sender::setFile);
connect(this, &MainWindow::startPrinting, senderWorker, &Sender::startPrinting);
connect(this, &MainWindow::stopPrinting, senderWorker, &Sender::stopPrinting);
connect(this, &MainWindow::pause, senderWorker, &Sender::pause);
connect(this, &MainWindow::setBaudrate, senderWorker, &Sender::setBaudrate);
connect(this, &MainWindow::openPort, senderWorker, &Sender::openPort);
connect(this, &MainWindow::closePort, senderWorker, &Sender::closePort);
connect(this, &MainWindow::injectCommand, senderWorker, &Sender::injectCommand);
connect(this, &MainWindow::flushInjectionBuffer, senderWorker, &Sender::flushInjectionBuffer);
senderThread->start();
senderThread->setPriority(QThread::TimeCriticalPriority);

(1)线程交互的设计

当我们看懂了这段代码以后就可以将它们抽象成我们自己的代码,即分解为:

以下是我基于上面的这个架构进行了简单的修改:

/*注册串口线程*/
void MainWindow::Register_Uart_thread()
{uartThread = new QThread(this);uartWorker  = new SerialThread(Sensor_Uart,Sensor_Baud);uartWorker->moveToThread(uartThread);connect(uartThread, &QThread::finished, uartWorker, &QObject::deleteLater);connect(uartWorker, &SerialThread::reportProgress, this, &MainWindow::updateFileProgress);connect(uartWorker, &SerialThread::update_layer, this, &MainWindow::updateModelLayer);connect(uartWorker, &SerialThread::send_debug_gcode_line, this, &MainWindow::GCode_Debug);connect(this, &MainWindow::startPrinting, uartWorker, &SerialThread::startPrinting);connect(this, &MainWindow::stopPrinting, uartWorker, &SerialThread::stopPrinting);connect(this, &MainWindow::pausePrinting, uartWorker, &SerialThread::pausePrinting);connect(this, &MainWindow::recovery_print, uartWorker, &SerialThread::recovery_print);uartThread->start();uartThread->setPriority(QThread::TimeCriticalPriority);
}/*注册文件管理线程*/
void MainWindow::Register_FileManage_thread()
{fileThread = new QThread(this);fileWorker = new file_manage();fileWorker->moveToThread(fileThread);connect(fileThread, &QThread::finished, fileWorker, &QObject::deleteLater);connect(this, &MainWindow::setGodeFile, fileWorker, &file_manage::parseFile);connect(fileWorker, &file_manage::update_gcode_info_display, this, &MainWindow::GCode_File_Info);connect(fileWorker, &file_manage::send_gcode_file, uartWorker, &SerialThread::setFile);fileThread->start();fileThread->setPriority(QThread::HighestPriority);
}/*注册协议解析线程*/
void MainWindow::Register_Protocol_Parse_thread()
{parserThread = new QThread(this);parseWorker = new protocol_parse();parseWorker->moveToThread(parserThread);connect(parserThread, &QThread::finished, parseWorker, &QObject::deleteLater);connect(parseWorker, &protocol_parse::hotbed_temp, this, &MainWindow::Display_HotBed_Temp);connect(parseWorker, &protocol_parse::hotend_temp, this, &MainWindow::Display_Hotend_Temp);connect(parseWorker, &protocol_parse::move_position, this, &MainWindow::Display_Move_Position);connect(uartWorker, &SerialThread::Data_Process, parseWorker, &protocol_parse::Data_Process);parserThread->start();parserThread->setPriority(QThread::HighestPriority);
}

(2)打印GCode文件与用户发送GCode命令的核心实现

(3)协议解析的核心实现

关于协议解析部分,我依然采用的是正则表达式的方案来实现,例如对温度部分的处理:

typedef struct
{QString raw;double e_c, b_c;double e_t, b_t;
} TemperatureReadings;//正则表达式获取温度
void MainWindow::Get_Temp_Test()
{int p = 0;QStringList data_list    ;TemperatureReadings r;/*对于单喷头打印机,它回复的数据格式是这样的*/QByteArray data = "T:41.88 /0.00 B:56.02 /60.00 @:0 B@:0 W:?";QRegExp temperatureRegxp("-?(([0-9]\\d*\\.\\d*)|(0\\.\\d*[0-9]\\d*)|([0-9]\\d*))");while ((p = temperatureRegxp.indexIn(data, p)) != -1){data_list.append(temperatureRegxp.cap(1));//上一个匹配的字符串的长度p += temperatureRegxp.matchedLength();}r.raw = QString(data);r.e_c = data_list[0].toDouble();   //喷头当前温度r.e_t = data_list[1].toDouble();   //喷头目标温度r.b_c = data_list[2].toDouble();   //热床当前温度r.b_t = data_list[3].toDouble();   //热床目标温度qDebug() << "解析源字符串:" << r.raw << " " << "解析长度:" << data_list.length();qDebug() << r.e_c ;qDebug() << r.e_t ;qDebug() << r.b_c ;qDebug() << r.b_t ;
}

例如对当前移动坐标的获取:

typedef struct
{QString raw;double x,y,z ;
} CoreXYZ ;//正则表达式获取坐标
void MainWindow::Get_GCodeXYZ_Test()
{int p = 0;QStringList data_list    ;CoreXYZ r;/*移动指令,例如解析下面这个指令*/QByteArray data = "G1 F1200 X-9.914 Y-9.843 E3.36222";QRegExp CoreXYZRegxp("-?(([XYZ]|[0-9]\\d*\\.\\d*)|(0\\.\\d*[0-9]\\d*)|([0-9]\\d*))");while ((p = CoreXYZRegxp.indexIn(data, p)) != -1){data_list.append(CoreXYZRegxp.cap(1));//上一个匹配的字符串的长度p += CoreXYZRegxp.matchedLength();}r.raw = QString(data);r.x = data_list[0].toDouble();r.y = data_list[1].toDouble();qDebug() << "解析源字符串:" << r.raw << " " << "解析长度:" << data_list.length();qDebug() << data_list[0];qDebug() << data_list[1];
}

(4)其它功能的设计

这部分就发挥大家自己的想象了,我先做了个测试版本,随便捣鼓一下,已经能够正常打印模型了:

目前这个项目还没有开源,我还在持续的完善中,希望最后能够助力一把 RepRapCalculator,希望加什么功能,大家可以在下方给我留言。

5、总结

要做属于自己的打印机,需要掌握以下技能:

  • 掌握QT软件开发(如果你会C#或者其它当然也没问题)

  • 掌握3D打印机GCode指令协议

  • 其它必要的知识,如设计模式、数据结构等。

另外,让我们对3D打印开源的小伙伴们为3D打印开源发发力量!

6、参考文献 & 引用

[1] G-Code Index. (n.d.). Marlin Firmware. https://marlinfw.org/meta/gcode/

[2] Neo, T. (2017, May 27). G-Code Index. Github. https://github.com/NeoTheFox/RepRaptor

[3] Neo, T. (2017, May 27). A Qt RepRap Gcode Sender/Host Controller. https://opensourcelibs.com/lib/repraptor

[4]卢华东. (2015, December 26). RepRap 3D Printer 入门介绍. https://blog.csdn.net/lu_embedded/article/details/50409510

往期精彩

开源Marlin2.x源代码架构学习笔记

步进电机驱动在3D打印应用的学习笔记(一)

3D打印过程与最近的学习成果

两个最常用的3D打印机切片软件

3D打印机marlin固件框架与GCode命令总结

3D打印机Marlin固件串口功能解析和程序移植

让野火F103开发板支持Marlin2.0固件是什么体验?3D打印主控板成员+1

C语言映射表在嵌入式串口解析、UI设计中的应用(值得收藏并实践的精华帖)

觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

如何打造属于自己的3D打印机上位机?这篇文章带你了解一下!相关推荐

  1. STM32+ESP8266连接电脑Qt网络上位机——QT篇

    本文简单介绍下手写网络调试器并连接ESP8266模块 上篇:  STM32+ESP8266连接电脑Qt网络上位机--准备工作 目录 一.部分Qt代码及实现过程 二.实现过程--使用ESP8266连接上 ...

  2. 上位机软件-串口助手-带登陆界面

    前言: 这是我在大三的时候写的一个串口助手,觉得挺好用的就分享给大家.如果有什么bug的话,希望大家多多包涵,毕竟对于C#语言我只是新手. 制作串口助手 串口助手选用Visual C#开发语言和Vis ...

  3. STM32F407--驱动OV2640采集图像数据,通过W5500传输至上位机---第一篇:OV2640简介及SCCB协议

    以下内容皆是个人学习过程中的总结,记录一下整个过程,用于后期复习,如有不对之处,麻烦各位大佬指出~ (喜欢的朋友麻烦点个关注~~~ 后期还会进行持续更新) OV2640概述: OV2640是OV (O ...

  4. prusai3打印机使用教程_prusa i3 3D打印机固件Marlin配置初学者篇

    前边的教程已经给大家介绍了如何组装Prusa i3打印机,不知道各位小伙伴有没有get到,既然打印机已经有了躯体,那么就需要一个灵魂.这个灵魂就是Marlin固件,记得刚开始了解3D打印机的时候,经常 ...

  5. 全球3D打印机行业一流服务品牌

    根据前瞻产业研究院的数据统计显示,2022年全球3D打印机市场规模将突破220亿美元,3D打印作为新兴的科技产业越来越成为人们关注的焦点,国内外3D打印企业如雨后春笋般崭露头角,3D打印领域竞争日趋激 ...

  6. Stratasys:光固化3d打印机优缺点论点,需品牌品质落实

    关于3d打印,早已不是什么新闻,在很多城市,它已经被应用于建筑,医疗等多个领域,甚至一些地方还有3d打印的网红景点,诸如,桥梁.雕像等.在越来越多的3d打印产品升级换代的同时,最早应用于市场的光固化3 ...

  7. 3D打印机可以打印什么?

    3D打印技术已经发展很好了,3D打印机可以打印什么?3dlabstore带你了解一下! 3D打印机打印出来的物件不仅仅具有漂亮的三维外观,实际上更多的有实用性功能.制造商可以为您提供一个3D模型模板, ...

  8. BCIduino转载|3D打印机使用的日常问题汇总

    本篇汇总一些日常的问题及解决办法. 参考了: https://www.sohu.com/a/413104768_120808704?trans=000014_bdss_dklzxbpcgP3p:CP= ...

  9. 上位机开发流程-小白开始

    上位机开发流程 文章目录 前言 一.上位机是什么? 二.开发步骤 1.了解需求 2.分析需求 3.设计软件模块 4.测试软件 5.交付及维护软件 总结: 前言 没有人可以真正理解另一个人. 你理解的也 ...

最新文章

  1. Lintcode27-Reverse 3-digit Integer
  2. 变,从南到北,从北到南...
  3. little w and Segment Coverage(差分)
  4. 推荐!国外程序员整理的 C++ 资源大全(转载)
  5. 响应文件是不是标书_标书的编制
  6. 卸载SD卡对MediaServer的处理
  7. 敏捷开发框架_RingCentral Tech丨LeSS- 大规模敏捷开发框架实践心路
  8. Microsoft Store下载应用奇慢无比的解决方案
  9. 基于STC89C52单片机的智能灯光毕业设计论文
  10. 规划计算机网络的成本估算,传统的项目管理软件包括进度计划、成本控制、资源调度和( )等功能模块。...
  11. 电子发票撤销 java_已确认的发票如何撤销
  12. LaTeX---字符操作
  13. java简单的猜数字游戏
  14. putty怎么连到linux服务器,PuTTY 从 Windows 连接到 亚马逊服务器Linux 实例
  15. File和path的用法
  16. GeoTools开发GIS图形界面应用程序
  17. 「解读」华为云桌面说“流畅”的时候,究竟在说什么?
  18. 2021.7.5-7.12 人工智能行业每周技术动态
  19. 韩信点兵python算法_韩信点兵和搬砖问题
  20. RedFlag 6.0 SP1的一些设置

热门文章

  1. 我的互联网价值观、方法论和个人实践(3)-衣食住行
  2. java数组转集合后添加元素报错
  3. ASP.NET Core WebAPI JWT Bearer 认证失败返回自定义数据 Json
  4. 国内邮箱排名VIP邮箱哪个好?VIP邮箱可以申请什么名称?
  5. python两个excel字段模糊匹配_Excel模糊查找中使用的算法 - python
  6. 仿真测试 | HIL测试简单介绍
  7. SQL查询日志 查看数据库历史查询记录的方法
  8. 【排序算法】交换排序(C++实现)
  9. 使用MITM调试线上前端错误
  10. 如何将手机里的wav录音转换成mp3格式?