眾所周知,對3D打印機感興趣的小伙伴來說,都清楚Cura是3D打印機的切片軟件,它的UI部分是基于QT來開發的。而Cura中很多功能其實是基于插件的形式來開發,其中,用于實現Cura的USB轉串口聯機打印的邏輯就是一個插件,它是使用Python語言來實現的,具體代碼位于:
https://github.com/Ultimaker/Cura/tree/main/plugins/USBPrinting
之前我也做了一些3D打印機的聯機打印的開源項目:
Anycubic Vyper 3D打印機串口屏改造開源項目之串口屏項目啟動篇(一)
Anycubic Vyper 3D打印機串口屏改造開源項目之QT溫度曲線顯示(二)
而我前陣子參加開放原子基金會組織的開發者成長激勵活動的作品其實也算是聯機打印的一種,只是實現的方式不同而已罷了:
開發者成長激勵計劃-基于TencentOS Tiny FDM 3D打印機云控制系統方案
說到Cura中的USB轉串口聯機打印,核心邏輯可以梳理下為以下幾點:
(1)查找串口設備列表并獲取對應的打印機設備端口號,這部分的代碼是在USBPrinterOutputDeviceManager.py這個文件里實現的。
(2)設置串口設備參數并連接設備、啟動更新線程來處理串口數據接收
具體的代碼實現如下:
defconnect(self): self._firmware_name=None#aftereachconnectionensurethatthefirmwarenameisremoved ifself._baud_rateisNone: ifself._use_auto_detect: auto_detect_job=AutoDetectBaudJob(self._serial_port) auto_detect_job.start() auto_detect_job.finished.connect(self._autoDetectFinished) return ifself._serialisNone: try: #設置串口參數 self._serial=Serial(str(self._serial_port),self._baud_rate,timeout=self._timeout,writeTimeout=self._timeout) exceptSerialException: Logger.warning("Anexceptionoccurredwhiletryingtocreateserialconnection.") return exceptOSErrorase: Logger.warning("Theserialdeviceissuddenlyunavailablewhiletryingtocreateaserialconnection:{err}".format(err=str(e))) return CuraApplication.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) self._onGlobalContainerStackChanged() self.setConnectionState(ConnectionState.Connected) #啟動更新線程 self._update_thread.start()
(3)啟動更新任務線程,更新任務線程的作用是處理以下幾件事情:
以readline()的方式去接收打印機回復的數據,然后處理數據,例如接收到了ok或者溫度信息等。
處理接收的數據,并接著發下一條Gcode指令,直到沒有得發為止。
處理打印過程中發生的異常事件
發送M105獲取溫度命令,這里Cura是做了一些處理的,發送該條命令的前提是打印機不處于忙狀態并且溫度到了設定的固件超時時間才會進行發送。Cura的超時設置為3s。
Gcode重發機制的實現
具體的代碼實現如下:
#線程_update_thread->更新任務函數的實現 def_update(self): whileself._connection_state==ConnectionState.Connectedandself._serialisnotNone: try: line=self._serial.readline() except: continue #獲取固件信息 #如果是Marlin,則會輸出類似如下所示的信息 #FIRMWARE_NAME:Marlin1.1.0.... ifnotself._firmware_name_requested: self._firmware_name_requested=True self.sendCommand("M115") #獲取FIRMWARE_NAME并保存起來 ifb"FIRMWARE_NAME:"inline: self._setFirmwareName(line) # time()是獲取時間戳,以秒作為時間間隔,這里的timeout是3,也就意味著,Cura發送獲取溫度的條件是: #1、當前的打印機不處于忙狀態 #2、超時,這里設置的時間是大于3s #以上兩個條件需要同時滿足 ifself._last_temperature_requestisNoneortime()>self._last_temperature_request+self._timeout: self.sendCommand("M105") self._last_temperature_request=time() #使用正則表達式獲取由打印機端上報的溫度事件,其中T:開頭的數據代表噴頭溫度,B:開頭的數據代表熱床溫度 ifre.search(b"[B|Td*]:?d+.?d*",line):#Temperaturemessage.'T:'forextruderand'B:'forbed extruder_temperature_matches=re.findall(b"T(d*):?(d+.?d*)s*/?(d+.?d*)?",line) #Updatealltemperaturevalues #獲取噴頭當前/目標溫度值并更新到前端顯示 matched_extruder_nrs=[] formatchinextruder_temperature_matches: extruder_nr=0 ifmatch[0]!=b"": extruder_nr=int(match[0]) ifextruder_nrinmatched_extruder_nrs: continue matched_extruder_nrs.append(extruder_nr) ifextruder_nr>=len(self._printers[0].extruders): Logger.log("w","Printerreportsmoretemperaturesthanthenumberofconfiguredextruders") continue extruder=self._printers[0].extruders[extruder_nr] ifmatch[1]: extruder.updateHotendTemperature(float(match[1])) ifmatch[2]: extruder.updateTargetHotendTemperature(float(match[2])) #獲取熱床當前/目標溫度值并更新到前端顯示 bed_temperature_matches=re.findall(b"B:?(d+.?d*)s*/?(d+.?d*)?",line) ifbed_temperature_matches: match=bed_temperature_matches[0] ifmatch[0]: self._printers[0].updateBedTemperature(float(match[0])) ifmatch[1]: self._printers[0].updateTargetBedTemperature(float(match[1])) #空行表示固件空閑 #多個空行可能意味著固件和Cura正在等待 #因為錯過了“ok”,所以我們跟蹤空行 #因為ok可能丟掉了,所以我們需要將空行記錄下來 ifline==b"": #Anemptylinemeansthatthefirmwareisidle #MultipleemptylinesprobablymeansthatthefirmwareandCuraarewaiting #foreachotherduetoamissed"ok",sowekeeptrackofemptylines self._firmware_idle_count+=1 else: self._firmware_idle_count=0 #檢查到ok字串或者_firmware_idle_count>1 ifline.startswith(b"ok")orself._firmware_idle_count>1: #此時打印機忙狀態解除 self._printer_busy=False #設置接收事件為True self._command_received.set() #如果當前命令隊列不為空,則從隊列取出一條命令往打印機串口繼續發送 ifnotself._command_queue.empty(): self._sendCommand(self._command_queue.get()) #如果處于正在打印中,則繼續發送下一條Gcode命令 #如果此時暫停標志生效,則什么事情都不干 elifself._is_printing: ifself._paused: pass#Nothingtodo! else: self._sendNextGcodeLine() #如果匹配到Marlin回復了"echo:busy"子串時,則設置打印機為忙狀態 ifline.startswith(b"echo"): self._printer_busy=True #如果在打印中接收到'!!',則表示打印機發出致命錯誤,這個時候需要直接取消打印 ifself._is_printing: ifline.startswith(b'!!'): Logger.log('e',"Printersignalsfatalerror.Cancellingprint.{}".format(line)) self.cancelPrint() #如果在打印中接收到"resend"或者"rs"這樣的字符串,則可以通過 Resend、resend 或 rs 請求重新發送。 elifline.lower().startswith(b"resend")orline.startswith(b"rs"): #AresendcanberequestedeitherbyResend,resendorrs. try: self._gcode_position=int(line.replace(b"N:",b"").replace(b"N",b"").replace(b":",b"").split()[-1]) except: ifline.startswith(b"rs"): #InsomecasesoftheRScommanditneedstobehandleddifferently. self._gcode_position=int(line.split()[1])
在USB轉串口聯機打印中,也實現了一些打印的基本業務,待后續分析和開源作品分享。
-
usb
+關注
關注
60文章
7945瀏覽量
264639 -
打印機
+關注
關注
10文章
771瀏覽量
45681 -
3D打印機
+關注
關注
9文章
526瀏覽量
44143 -
Cura
+關注
關注
0文章
5瀏覽量
2651
原文標題:3D打印機USB聯機打印是如何實現的?(以Cura插件USBPrinting為例)
文章出處:【微信號:嵌入式應用研究院,微信公眾號:嵌入式應用研究院】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論