Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filament Jammed Sensor - Rotary Encoder, Hall effect, Optical, #122

Open
Everlanders opened this issue Apr 4, 2018 · 22 comments
Open

Filament Jammed Sensor - Rotary Encoder, Hall effect, Optical, #122

Everlanders opened this issue Apr 4, 2018 · 22 comments

Comments

@Everlanders
Copy link

I have a CR10s that I got to print some large ABS parts and really love this plugin, thanks.

I am however having problems with the filament jamming for unknown reasons, it's not tangled, it's just not moving, the gear has chewed a hole in the filament halfway through the night and it's air-printing. Frustrating because it had been printing fine for 20 - 30 hours. I have 11 failed prints like this and I have an idea that I think will be simple to implement.

There are many ways to sense filament movement, the easiest being a cheap hall effect sensor on a wheel or a IR sensor on a pinwheel - I intend to modify a flow meter for water once I finish typing this message, but they mostly all use pulse output.

It would be very helpful if there was an option to monitor a pin for these pulses and when they stop, pause the print just like the loss of filament detection. Just one option, How long to wait after last state change.

Thank you.

@Everlanders
Copy link
Author

Last night I built 2 variations of these sensors, one using IR and one using a modified flow meter.

https://photos.app.goo.gl/Oumt1j2d1gzG0ZQC2

The flow meter just has it's paddles clipped off and replaced with a rubber grommet that acts as a pulley, grabbing the filament on 3 sides. The IR is better because it has more pulses per revolution - the flow meter only has 2.

The Arduino counts the pulses every 30 seconds and if less than 1, opens the relay which is wired to the stock filament sensor. This is less than ideal because you need to jumper the relay to get it started. Would be better if the plugin could watch for it only while printing.

@vitormhenrique
Copy link
Owner

vitormhenrique commented Apr 6, 2018

I feel like using an encoder and checking if it is sniping is the best choice. right now I don't have the time for anything really on this plugin, I'll put on the road map, but what you can defenetlly do, is create a sensor, with an arduino pro mini, or just a bare ATtiny and program the sensor logic on it, if it stop sensing it would set a pin high or low, and one input pin to enable or disable the sensor.
On the plugin side, you configure the output of the sensor as a regular filament sensor, and on the enable / disable pin configure it as an output on the plugin and to start it when the print starts.

@Everlanders
Copy link
Author

Version 3:

https://photos.app.goo.gl/L3ogR4uD734MpQl22

I've got a small Arduino dealing with it, but I thought it would be a useful thing for others, more plug and play.

Thanks again!

@vitormhenrique
Copy link
Owner

Are you using the ir sensor? have a link for the parts?

@Everlanders
Copy link
Author

Everlanders commented Apr 6, 2018

You bet! eBay Link

In case that listing/link doesn't work in the future, search for Slot Type IR Optocoupler Speed Sensor Module LM393 for Arduino

@vitormhenrique
Copy link
Owner

How about the wheel? We would need to make a instruction on how to create the sensor, and document parts etc...
I have access to a laser cutter and can create a wheel, but not everyone has...

@Everlanders
Copy link
Author

I salvaged it from a Roomba, but it could easily be 3D printed. I can make a video if the plugin supports it. ;-)

I'll put together some files tonight.

@Everlanders
Copy link
Author

These are even nicer...

@maukcc
Copy link

maukcc commented Jun 21, 2018

The MK4duo firmware has this option already installed (for up to 6 extruders) called Extruder Encoder Control.
We have not tested it, but they say it works.

@jlg89
Copy link

jlg89 commented Jul 9, 2018

I'm working on the same type of enhancement, and have purchased a couple of small magnetic encoder sets:
https://www.pololu.com/product/3081
They're intended for small motors, but I think I can build a small enclosure that could be used like a typical filament sensor, or perhaps contact the spool itself to detect motion there. Monitoring the spool would put the detection farther from the extruder, which is often a good thing.

There has to be a configurable delay before activating the sensor when the print starts, because the filament won't move during things like homing, bed leveling, etc. that typically occur at the beginning of a print. So (as you noted above) this new input type would also need a "startup delay" option like the outputs already have.

Another option might be to add an input type that is able to monitor either the output of a shell script, or the presence of a particular file. That would enable us to use the Pi's IO capabilities for this sort of thing, instead of having to set up another control system.

@vitormhenrique
Copy link
Owner

Probably the best to implement is not a delay, because you would need to tweak the time to get it just right, I think the best implementation is "after the first change" on the encoder the filament sensor is activated, and a timeout of some minutes if it does change at all to catch you starting a print with a jammed extruder.
BUT don't have a nice setup to test and implement and I have been spending a lot of money on other projects that I'm working, so I don't plan to buy new encoders / sensors to implement this, if someone works a decent prototype for a filament sensor with encoders and ship it to me I'll gladly take the time to implement, but if not we will have to wait until I finish current projects....

@jlg89
Copy link

jlg89 commented Jul 9, 2018

I bought extra encoder sets. If I come up with something interesting, I'll send you one.

@jlg89
Copy link

jlg89 commented Jul 13, 2018

Here's a shell script that can be run on the Raspberry Pi, and triggered by an Enclosure Plugin action if desired. Working on a couple of different hardware solutions, one that tracks the filament itself (like a traditional switch-based filament sensor) and another that monitors spool movement. Will post details later. Both are Hall effect rigs, but any Pi-compatible pulsing output (optical, mechanical, etc.) could be used with this script.

Update 7/25: I discovered that the "inotifywait" command is unreliable for monitoring RPi GPIO status. Long story, but I rewrote the script using a subroutine that does the job without incurring much CPU overhead at all. Still need to convert it to python.

Update 7/31: Added a pre-pause check to query OctoPrint for the tool0 set point. If that is 0.0, the script assumes the print is finished and in cool-down mode, so it exits. Still need to convert it to Python...

Update 8/29: Modified the script to accept a timeout value on the command line, so you can specify the timeout in the Enclosure Plugin script action. Also added a wrapper script, which starts the main script in the background and returns info to EP.

Main script:

#!/bin/bash
#
# /home/pi/FilamentMonitor.sh -- Jon L. Gardner, jon@brazoslink.net
#
# Monitors a Raspberry Pi GPIO input for pulses from a filament motion sensor.
# Triggers a print pause via OctoPrint API, and optionally notifies via IFTTT.

# select RPi input pin to monitor for motion sensing
GPI=5

# OctoPrint API key
apikey=YOUR_OCTOPRINT_API_KEY

# IFTTT API key and event name (from Enclosure Plugin settings)
ifttt_key=YOUR_IFTTT_API_KEY
ifttt_event=YOUR_IFTTT_EVENT_NAME

# should not need to change anything below this line, unless you want a different default timeout

# set timeout in seconds
timeout=$1

if [[ ! $timeout ]]; then
 /bin/echo "Warning: No timeout specified, defaulting to 30s."
 timeout=30
else
 /bin/echo "Timeout set to ${timeout} seconds."
fi

# prepare the pins
IO=/sys/class/gpio
if [[ $GPI ]]; then
 /bin/echo "Configuring input on BCM ${GPI}."
 if [ ! -d $IO/gpio${GPI} ]; then
  /bin/echo "${GPI}" > $IO/export
 fi
 /bin/echo "in" > $IO/gpio${GPI}/direction
else
 /bin/echo "Error: No GPIO pin specified!"
 exit 1
fi

function trap_ctrlc ()
{
  /usr/bin/printf "\nLongest pulse time was $maxtime seconds.\n"
  exit 2
}

function monitor_gpio ()
{
   ok=0
   exp=0
   val1=$(/bin/cat $IO/gpio${GPI}/value)
   while [[ $ok = 0 ]]; do
    val2=$(/bin/cat $IO/gpio${GPI}/value)
    if [[ $val1 != $val2 ]]; then
     val1=$val2
     ok=1
     exp=0
    fi
    if [ $init == 0 ]; then
     ctime=`/bin/date +%s`
     etime=`/usr/bin/expr $ctime - $stime`
     if [ $etime -gt $timeout ]; then
      ok=1
      exp=1
     fi
    fi
    sleep 0.2
   done
}

function check_print_status ()
{
  # query OctoPrint for the main tool setpoint
  done=0
  setpoint=`/usr/bin/curl -s -H "X-Api-Key: "$apikey"" http://localhost/api/printer | /bin/grep -A3 tool0 | /bin/grep target | /usr/bin/cut -f2 -d:`
  # if 0.0, assume the print is done
  if [ $setpoint = 0.0 ]; then
   done=1
  fi
}

trap "trap_ctrlc" 2
init=1
maxtime=0

while :
do

 if [ $init == 1 ]; then
  # wait for the first change before starting the timer
  /bin/echo "Monitoring BCM ${GPI} for initial filament movement..."
  monitor_gpio
  /bin/echo "Initial pulse detected."
  /bin/echo "Monitoring with timeout of $timeout s..."
  init=0
 fi

 # once things are moving, detect if things stop moving for longer than the timeout
 stime=`/bin/date +%s`
 monitor_gpio

 if [ $exp == 1 ]; then

  /bin/echo "Filament has stopped moving."

  check_print_status

  if [ $done == 0 ]; then
   # pause the print via OctoPrint API (assumes localhost)
   /bin/echo '{"command": "pause" , "action": "pause"}' | /usr/bin/curl -s -X POST -d @- -H "X-Api-Key: "$apikey"" -H "Content-Type: application/json" http://localhost/api/job > /dev/null

   # notify via IFTTT
   if [[ $ifttt_key ]]; then
    /bin/echo '{"value1": "Printer paused by filament motion sensor." , "value2": "Longest pulse delay '$maxtime' s."}' | /usr/bin/curl -s -X POST -d @- -H "Content-Type: application/json" https://maker.ifttt.com/trigger/$ifttt_event/with/key/$ifttt_key > /dev/null
   fi

   # wait for the timeout period, then start over
   /bin/echo "Pausing $timeout s for reset..."
   /bin/sleep $timeout
   init=1
  else
   # assume the print is finished, and exit
   echo "Print appears to be finished."
   trap_ctrlc
  fi
 else
  #  maintain record of longest pulse time
  ftime=`/bin/date +%s`
  ttime=`/usr/bin/expr $ftime - $stime`
  /usr/bin/printf "."
  if [ $ttime -gt $maxtime ]; then
   maxtime=$ttime
  fi

 fi

done

Wrapper script:

#!/bin/bash
#
# /home/pi/startFM - wrapper script for FilamentMonitor.sh
#

# set log file path to whatever you want
LOG=/home/pi/fm.log

# kill any running FilamentMonitor processes
while :
do
pid=`ps ax|grep -v grep|grep FilamentMonitor|cut -c1-5`
if [[ $pid ]]; then
 sudo kill $pid
else
 break
fi
done
# set timeout from command line
timeout=$1
if [[ ! $timeout ]]; then
 timeout=30
fi
cd /home/pi
# to monitor progress, ssh to the OctoPi and "tail -f" your log file
nohup ./FilamentMonitor.sh $timeout > $LOG &
echo "FilamentMonitor started (PID $!) with ${timeout}s timeout."
echo "Logging to $LOG"

Example Enclosure Plugin output config:

Example EP output config

@vitormhenrique
Copy link
Owner

@jlg89 thanks for the script, despite is helpful as a general guide on how to implement, if you can convert it to python it would be much more helpful, because it would be easier to port.

Also, python has some nice features for the GPIO, like interrupts for the pins, making easy to get exact time that the GPIO change status.

@jlg89
Copy link

jlg89 commented Jul 25, 2018

@vitormhenrique if you can give me a shipping address, I'll send you one of the pulse-generating filament sensors I built.

@jlg89
Copy link

jlg89 commented Jul 26, 2018

Here's a PDF writeup on a couple of filament motion sensors I've put together.

Filament Motion Sensors.pdf

@vitormhenrique
Copy link
Owner

@jlg89 send me an email on the email linked to my profile and I'll give you my address....

@starbuck93
Copy link

@jlg89 Thanks for that writeup, you should make a new repo for it! I just bought a few of the https://www.pololu.com/product/3081 sets to try to use your enclosure with a Duet board.

@jlg89
Copy link

jlg89 commented Mar 26, 2019

OK...finally, here's my first shot at a Python version of the script. This is incomplete, missing the printer pause & notification actions, but the basic pulse input detection is there.

#!/usr/bin/env python2.7

import sys
import time
import urllib2
import json
from collections import namedtuple
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)

CHAN=5
TIMEOUT=30
OCAPIKEY='your_OctoPrint_API_key'

GPIO.setup(CHAN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

global MOV
MOV=0
RUN=0
DONE=0

# increment MOV at every state change of the monitored channel
def my_callback(channel):
    global MOV
    MOV += 1

GPIO.add_event_detect(CHAN, GPIO.BOTH, callback=my_callback, bouncetime=300)

def PrintDone(apikey):
   # check OctoPrint API for main tool set temp, if 0.0 assume job finished
   url="http://localhost/api/printer/tool?apikey={0}".format(apikey)
   response = urllib2.urlopen(url)
   html = response.read()
   x =json.loads(html, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
   tempt=x.tool0.target
   if tempt==0.0:
    return 1
   else:
    return 0

try:

   while DONE==0:
    MOV=0
    RUN=0
    print('Waiting for initial pulse.')
    # GPIO event detect will increment MOV
    while RUN==MOV:
     # wait for initial pulse
     time.sleep(.1)
    print('Pulse detected.')
    # monitor I/O for state changes
    while True:
     start_time = time.time()
     MOV=0
     RUN=0
     # output a dot at each state change
     sys.stdout.write('.')
     sys.stdout.flush()
     while RUN==MOV:
      if time.time() - start_time > TIMEOUT:
       print('Filament has stopped moving.')
       if PrintDone(OCAPIKEY):
        print('Print appears to be finished.')
        DONE=1
        break
       # timeout actions go here -- pause print, notify via IFTTT etc.
       break

except KeyboardInterrupt:
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit

@sciaio
Copy link

sciaio commented Jul 12, 2019

Really nice, are there any more progress? Will be available into the plugin soon?

@peterloron
Copy link

Hi. I'm going to play with the script @jlg89 posted (thanks!), but wondering what the status of this functionality is in the main plugin? Thanks!

@vitormhenrique
Copy link
Owner

The status is really really low priority, I started masters on computer science and have been busy with other projects, I plan to re-write the entire base Conde of the plugin to make it easier to understand and expand, so maybe more people would be able to push commits.
I wanna re-write the code with a better objected oriented mentality and add some of the features that are more commonly requested. Unfortunately it's not the case of this. One day I might get to this and figure a way to implemented, but honestly is not on a close road map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants