Preventing failed prints with filament feed encoder and Arduino

Print failures suck but they can be early warnings of underlying issues with your 3D printer. Frustrated by print failures, I put together an alarm system that alerts me when the filament stops moving. After successfully printing a model that shouldn't have worked, I learned that band-aid solutions are just that, temporary solutions, but reveal a lot about the nature of the problem.

The assembly is made up of a rotary encoder, some thin foam tape, and some 3D printed parts I designed. The wheel diameter is 2.5-inches that was eye-balled as the minimum radius the ABS can easily contort to.

This was built in an afternoon and shouldn't be considered complete. Most likely, this will be scraped by the end of the month and rebuilt from everything I've learned. Iteration is key to great designs, and testing a hypothesis quickly is imperative. I took a lot of shortcuts and hopefully you, the reader, will appreciate this assembly for the idea rather than ignore it because of its craftsmanship.

Not shown here, a cable tie was super glued inside to close up tolerances. It was was faster than reprinting the part.

 The Arduino relays the rotary encoder data to the computer over USB

The Arduino relays the rotary encoder data to the computer over USB

An Arduino is used to listen to the grey code from the rotary encoder and send the data over serial to a Python script. The Python measures the time since the last update from the Arduino, and annoyingly 'beeps' three times when it exceeds 15 seconds.

I've graphed two 2-hour prints worth of data below. The first half has five tall spikes where I paused the printer to fix the jams. The second half is after I replaced the entire print head assembly and the problems seem to subside.

Notice the striations in the parts before the new print head. The bottom half should have never been able to print, but the encoder was able to warn me every time a jam occurred.

It's obvious the quality improvement the new print head offers but the data gives a clue as to why. The number of false positives, where the alarm sounded even though it continued to work, drastically decreased from 14 to 3 after changing the print head. This is consistent with the striations assuming they're an effect of the filament extruding at inconsistent diameters. My guess at this point is the nozzle is partially clogged.

Conclusions

My system works fantastic but I want to use an optical encoder for the next version. It's the obvious choice and if I had one at the time I put this together I would have used it. The filament feed rate is the best metric to indicate problems but the rotary encoder I have is barely precise enough. When printing layers with small features, the printer can slow down and cause false positives to occur. This is visible in the data at the end of the prints since the final few layers need slow feed rates and show up as spikes in the data. A more precise encoder can lead this project to pause prints on its own with 99% accuracy and finally give the peace of mind that the printer is somewhat fault tolerant.

Python Code:

import serial
import sys
import time
import os

lastCount = 0
newCount = 0
tempTime = time.time()
warned = False
printStarted = 0;

feedrateTimeWarning = 15 #seconds

ser = serial.Serial('/dev/tty.usbmodem14541', 9600,timeout=0)

while(True):
    newCount = ser.readline()
    if len(newCount) != 0:
    
        if printStarted == 0:
            printStarted = time.time()
    
        if newCount > lastCount :
            print "+ ", round(time.time() - tempTime,2),"s"
        if newCount < lastCount :
            print "-", round(time.time() - tempTime,2),"s"
            
        if newCount != lastCount :
            tempTime = time.time()
        
        warned = False
        time.sleep(0.5)
    
        #print time.time() - tempTime
    
    if (time.time() - tempTime > feedrateTimeWarning and printStarted != 0):
        if warned == False:
            print "******** WARNING - Check Feedrate - ", time.time()," ******"
            os.system("python play_wav.py beep-01a.wav")
            time.sleep(1)
            os.system("python play_wav.py beep-01a.wav")
            time.sleep(1)
            os.system("python play_wav.py beep-01a.wav")
            warned = True