1、方案摘要
1.1、前言
3D打印技术是增材制造的典型体现。它的原理是通过特定的切片软件将产品的3D设计模型图转换成3D打印机控制系统可以识别的控制代码,该控制代码在导入3D打印机控制系统后,3D打印机控制系统通过解析每一行控制代码,进而控制3D打印机打印模型。例如FDM打印机的打印原理,如下图1-1所示。
STL/Obj(当然还有很多种格式)文件就是模型本身的文件,它可能是由三维模型设计师、结构工程师设计出来的模型文件,也可能是通过3D扫描仪扫描出来的模型文件,而GCode就是通过切片软件将这些STL/Obj等模型文件切出来的一堆能够让3D打印机识别并运行的指令,它会告诉3D打印机应该如何去运行,例如以下是通过FDM Cura切片软件切出来的开源模型文件(如下图1-2所示)-可动盘龙的GCode代码(如下图1-3所示):
可动盘龙切片后生成的GCode文件:
开源下载地址:
代码语言:javascript复制https://www.bilibili.com/video/BV1HM4y1A73r?spm_id_from=333.337.search-card.all.click
目前,3D打印技术在全球增材制造领域已经发展十分成熟。而我国的3D打印技术与欧美国家相比相对来说较为落后,具体体现在3D打印机技术在物联网方面的应用。目前,我国市场上的3D打印机大部分都不具备网络控制和管理功能,不能更好地与现代物联网信息时代接轨。
本次参加开发者成长激励计划的作品是基于TencentOS Tiny的FDM 3D打印机云控制系统方案,该方案初步构思一个基于FDM 3D打印机的云控制交互系统,实现对FDM 3D打印机的控制、信息交互等基本功能。相对于3D打印机本身来说,本次参赛作品的云控制系统方案相当于是一个上位机,而上位机的体现方式可以是串口屏、物联网通讯模块等其它任何组成形式。市面上常见的主流串口屏式3D打印机主流通信架构如下图1-4所示:
该方案的设计初衷是为TencentOS-tiny与腾讯云IoT结合在3D打印物联网方面的应用打下一定的基础。TencentOS-tiny与IoT Explorer结合在嵌入式设备端具备完善的从端到云,从云到端的一体化解决方案。因此,在未来,有TencentOS-tiny与IoT Explorer物联网平台的赋能加持,3D打印物联网方面的应用一定能够有更好的发展和创新的应用场景,为智能制造、工业集群、人工智能等行业做出更多的贡献,进而帮助企业提升研发和生产效率,降低人工和生产成本,实现规模经济。
1.2、方案亮点
- 软件业务设计解耦,可拓展性强
- 支持多平台快速适配与工程迁移
- 支持腾讯连连WIFI Smart Config扫码配网功能
- 支持腾讯连连小程序数据上行(温度、坐标、调平数据等)
- 支持腾讯连连小程序数据下行(温度、移动轴、风扇速度设置等)
- 支持腾讯云IoT Explorer平台实时查看上报数据信息(对标腾讯连连小程序数据上行)
- 支持腾讯云IoT Explorer平台下发控制指令(对标腾讯连连小程序数据下行)
- 支持腾讯连连微信公众号、腾讯连连小程序移动轴、喷头温度过高信息预警信息推送
- 首次采用腾讯云可视化编辑器进行腾讯连连小程序界面的布局、事件、属性关联等。
- 实现3D打印机的基本控制和交互功能,未来可拓展成为云打印方式,实现3D打印机物联网打印。
1.3、方案演示
项目Github仓库地址:
代码语言:javascript复制https://github.com/Yangyuanxin/IoT_3DPrinter
项目Gitee仓库地址:
代码语言:javascript复制https://gitee.com/morixinguan/IoT_3DPrinter
功能演示地址:
代码语言:javascript复制https://cloud.tencent.com/developer/video/32150
2、方案构成
2.1、硬件部分
硬件部分主要分为3D打印机(Anycubic Vyper 3D打印机)和3D打印机联网控制系统( 基于沁恒RISV-V MCU CH32V307VCT6芯片的CH32V_EVB开发平台)两部分,其中CH32V_EVB(如图2-2所示)作为上位机,而Anycubic Vyper 3D打印机则作为下位机,如下图2-1所示:
Anycubic Vyper是一款优秀的3D打印机,不管是外观、打印速度、打印质量而言都相当不错。其固件源代码是基于著名的Marlin开源固件进行开发,它的源代码也是开源的,开源仓库地址:
代码语言:javascript复制https://github.com/ANYCUBIC-3D/Vyper
用户可根据自身需求定制和修改Marlin固件来实现3D打印机的自定义功能,甚至也可以结合其它配件改装成为雕刻机等方案。CH32V_EVB是腾讯本次TencentOS Tiny RISC-V IoT训练营结合沁恒推出的RISV芯片方案的开发板,如下图2-3所示:
沁恒CH32V307开发板板载资源丰富,并且该开发板支持E53传感器以及WAN口,可拓展传感器和IOT模块实现不同的行业解决方案Demo。
2.2、软件部分
软件部分是将3D打印机与TencentOS Tiny、腾讯云IoT Explorer、腾讯连连小程序进行结合,从而实现3D打印机物联网控制交互、数据交互的基本功能,其主要由以下三个部分构成,如下图2-2所示:
3、方案核心设计
方案的核心主要在于软件部分的设计,主要包括腾讯连连小程序部分以及嵌入式设备端部分。
3.1、腾讯连连小程序部分
腾讯连连小程序部分采用的是IoT Explorer团队提供的最新的可视化面板编辑器来实现,可视化面板编辑器相对于标准面板(如图3-1所示)来说,控件种类和可操作性更加强大,即使开发者不懂微信小程序的开发,只需结合数据模版所提供的功能属性,即能够通过可视化面板编辑器轻松、快速做出功能丰富、样式好看、人机交互体验感超好的交互界面。
由于时间关系,目前仅用可视化面板编辑器规划了两个页面,如下图3-2所示:
3.2、嵌入式设备端部分
嵌入式设备端部分主要包括TencentCloud IoT Explorer、APP、Common、OperatingSystem、Hardware、Bsp、McuPlatform这几个部分组成。其软件整体框架组织设计理念是为了方便项目的可持续功能迭代和未来的发展,如下图3-3所示:
(1)TencentCloud IoT Explorer
腾讯IoT Explorer物联网设备管理平台、集成腾讯连连小程序、微信公众号推送等物联网业务。
(2)APP
主要为本项目的业务逻辑部分,主要分为串口数据解析、GCode命令转发、Mqtt订阅和发布业务处理等。
(3)Common
主要集成了提供给项目使用的软件组件,其中包括ringbuffer、cJSON、Mqtt、Log系统等其它组件。
(4)OperatingSystem
操作系统部分,目前仅支持TencentOS-tiny,TencentOS-tiny是一个内存占用极小、组件丰富、扩展性极强且效率极高的物联网实时操作系统。
(5)Hardware
主要实现了一些完成项目必备的硬件驱动程序,包括按键驱动、LCD驱动、LED驱动、WIFI驱动等。
(6)Bsp
主要是由Mcu平台提供的板级支持包,目前该部分仅支持CH32V_EVB平台,未来可继续添加更多平台提供的Bsp。
(7)McuPlatform
具体的Mcu平台,目前该部分仅支持CH32_EVB平台,未来可继续支持更多的平台。
3.3、主业务流程详细设计
APP主业务流程分别创建三个线程来进行不同业务的处理,而线程间通信机制采用TencentOS-tiny提供的消息队列进行交互通讯,当线程没有接收到消息时,该线程为阻塞等待状态而不消耗CPU时间,直到接收到消息时,线程才恢复就绪态,对接收的数据进行处理。这样做的好处是能够高效的管理CPU资源,避免CPU资源浪费。
在APP主业务流程设计中,采用消息队列通讯也是一种软件设计解耦的实现方式,线程与线程之间通过消息队列通信,能够实现业务隔离,让整个系统的可拓展性大大提高。除此之外,采用消息队列的好处是能够携带数据载体,数据格式也可以由用户自定义。
本次参赛的作品主业务流程设计思路如下图3-4所示:
其中,消息队列之间的数据传输设计是基于一个消息结构体来进行的,如下所示:
代码语言:javascript复制/*消息结构体封装*/
#define MSG_LEN 50
struct Msg_t
{
//消息类型
uint8_t Type;
//消息负载
char Data[MSG_LEN];
};
而消息类型则是由各种命令构成,如下所示:
代码语言:javascript复制//往网络线程发送的消息类型
enum MsgCmd_t
{
MSG_CMD_UPDATE_TEMP=0,
MSG_CMD_UPDATE_AXIS,
MSG_CMD_UPDATE_TOKEN,
MSG_CMD_LEVELING_1,
MSG_CMD_LEVELING_2,
MSG_CMD_LEVELING_3,
MSG_CMD_LEVELING_4,
};
//往GCode转发线程发送的消息类型
enum Msg2GCode_t
{
MSG_2_GCODE_CMD_AUTO_GET_TEMP = 0,
MSG_2_GCODE_CMD_PLA_PRE,
MSG_2_GCODE_CMD_ABS_PRE,
MSG_2_GCODE_CMD_TEMP_DROP,
MSG_2_GCODE_CMD_X_MOVE_ADD,
MSG_2_GCODE_CMD_X_MOVE_SUB,
MSG_2_GCODE_CMD_Y_MOVE_ADD,
MSG_2_GCODE_CMD_Y_MOVE_SUB,
MSG_2_GCODE_CMD_Z_MOVE_ADD,
MSG_2_GCODE_CMD_Z_MOVE_SUB,
MSG_2_GCODE_CMD_ZERO_OF_X,
MSG_2_GCODE_CMD_ZERO_OF_Y,
MSG_2_GCODE_CMD_ZERO_OF_Z,
MSG_2_GCODE_CMD_ZERO_OF_ALL,
MSG_2_GCODE_CMD_LEVEL_DATA,
MSG_2_GCODE_CMD_FAN_SETTING,
MSG_2_GCODE_CMD_START_PRINT
};
消息类型的负载可能是没有任何内容的,可能是字符串类型,也可能是结构体类型,也可能是其它类型。要接收什么类型的数据,一般由消息类型决定,但最终如何实现也取决于用户的业务逻辑。因此,这样的设计方案可拓展性非常强。
3.3.1、主线程任务处理
(1)完成消息队列、网络线程、GCode转发线程的创建。
代码语言:javascript复制#define DEFAULT_TASK_SIZE 2048
void StartMainTask(void *pdata);
osThreadDef(StartMainTask, osPriorityLow, 1, DEFAULT_TASK_SIZE);
#define GCODE_FORWARD_TASK_SIZE 1024
void StartGCodeForWardTask(void *arg);
osThreadDef(StartGCodeForWardTask, osPriorityHigh, 1, GCODE_FORWARD_TASK_SIZE);
#define MQTT_TASK_SIZE 2048
void StartNetworkTask(void *pdata);
osThreadDef(StartNetworkTask, osPriorityLow, 1, MQTT_TASK_SIZE);
//主线程往网络线程的消息队列
k_msg_q_t DataMsg;
uint8_t DataMsgPool[50];
//网络线程往GCode转发线程的消息队列
k_msg_q_t GCodeMsg;
uint8_t GCodeMsgPool[50];
.....省略部分代码
//创建主线程往网络线程的消息队列
err = tos_msg_q_create(&DataMsg, DataMsgPool, 50);
if (K_ERR_NONE != err)
{
printf("Create DataMsg Fail:%d!n", err);
return;
}
//创建网络线程往GCode转发线程的消息队列
err = tos_msg_q_create(&GCodeMsg, GCodeMsgPool, 50);
if (K_ERR_NONE != err)
{
printf("Create GCodeMsg Fail:%d!n", err);
return;
}
//创建网络线程
if (NULL == osThreadCreate(osThread(StartNetworkTask), NULL))
{
printf("osThreadCreate Mqtt Fail!n");
return;
}
//创建GCode转发线程
if (NULL == osThreadCreate(osThread(StartGCodeForWardTask), NULL))
{
printf("osThreadCreate StartGCodeForWardTask Fail!n");
return;
}
(2)向打印机发送自动获取温度命令,用于定时获取打印机喷头、热床的温度数据上报。
发送自动获取温度的命令主要是基于GCode命令传输协议,而3D打印机的通信方式就是基于GCode命令进行传输的,当我们向3D打印机串口发送GCode代码 n(换行),3D打印机串口接收到指令时,内部固件会对下发的指令进行处理并回复对应的数据。因此,我们可以到Marlin的官网上找到温度获取对应的GCode命令并在程序中进行设置,这样打印机就能够自动上报喷头、热床的温度了。以下是Marlin官方网站GCode指令查询:
代码语言:javascript复制https://marlinfw.org/meta/gcode/
3D打印机获取温度有两种形式,一种可以是专门开一个3-4s的定时器,定时发送M105命令给3D打印机,这样3D打印机收到M105指令后,会返回温度数据。而Marlin官方并不建议采用这样的方式来获取温度,而是建议发送M155指令来让温度自动上报。因此,我们可以使用M155指令来避免这个问题,前提是3D打印机固件程序开启了AUTO_REPORT_TEMPRATURES宏,否则,我们就需要去修改3D打印机固件,以支持温度自动定时上报命令。使用方法如下图3-6所示:
此部分的代码逻辑如下所示,先设置自动获取温度的消息类型,然后通过消息队列将消息转发给GCode转发线程,具体转发的内容到GCode转发线程部分会进行讲解,大致代码逻辑如下所示:
代码语言:javascript复制/*M155 - Temperature Auto-Report*/
for (int i = 0; i < 3; i )
{
//设置消息类型,不需要带数据负载
Msg.Type = MSG_2_GCODE_CMD_AUTO_GET_TEMP;
//向GCode转发线程发送消息
tos_msg_q_post(&GCodeMsg, (void *) &Msg);
}
.....省略部分代码
(3)获取打印机上报的GCode命令回复的数据并进行解析,并进行温度等数据的显示以及将温度、移动轴坐标等信息通过消息队列转发给网络线程进行Topic发布。
其中,GCode数据回复需要根据预判的信息进行处理,例如解析喷头和热床的温度数据,通过串口我们能够得知打印机上报的温度数据格式如下:
代码语言:javascript复制 T:27.00 /0.00 B:27.80 /0.00 @:0 B@:0(末尾是n) //The first byte is a space
ok T:27.03 /0.00 B:27.78 /0.00 @:0 B@:0(末尾是n) //The first two bytes consist of ok
数据的回复是从串口接收中断过来的,然后主线程通过读取环形队列的数据,再进行处理,大致逻辑如下:
代码语言:javascript复制void UART7_IRQHandler(void)
{
uint8_t data;
osSchedLock();
if (USART_GetITStatus(UART7, USART_IT_RXNE) != RESET)
{
data = USART_ReceiveData(UART7);
//将接收的每一个字节写入环形队列
ring_buffer_write(data,&Fifo);
}
osSchedUnLock();
}
//主线程
void StartMainTask(void *arg)
{
//......省略部分代码
for (;;)
{
//从环形队列里取数据,存到缓存里,然后进行处理
if (0 == ring_buffer_read(&data, &Fifo))
{
//判断是否有一行
if (data != 'n')
GCodeReplyBuff.GCodeLineBuff[GCodeReplyBuff.GCodeLineCount ] = data;
else
{
GCodeReplyBuff.GCodeLineBuff[GCodeReplyBuff.GCodeLineCount] ='