3D printer filament sensor

3 minute read

Using an optical end-stop to detect filament breakage

An optical endstop will be used to add filament break detection to a 3D printer. To add this functionality a Raspberry Pi running Octoprint and its GPIO headers will be used. If you’re not already using Octoprint I highly recommend it. My install loosely follows Moonshine’s guide. Their writeup is good and works out of the box with minimal fuss as long as you are careful with editing the configuration.yaml file. A housing for this salvaged endstop was designed and printed in ABS.

With the filament inserted the output is pulled to ground:

With the filament removed, the output is pulled to vcc:

This endstop works on a normally closed (inverted) state. Its operation is incompatible with the plugin for Octoprint. This problem can be easily rectified. A simple transistor not-gate can be built or the library itself can be altered for this change. There were few resources on the internet for making the necessary adjustments to the plugin, so the changes will be made and they’ll be documented below.

The end stop was mounted via a spacer block and the wiring was tucked into the stock loom:

The hardware is basically finished and ready to be used. The output is placed on GPIO17, this opticial endstop uses the Pi’s 3v3 bus. In this directory the plugin is installed:


Here the init.py file can be found and edited.

To invert the input start at the function called check_status. Here the 1 is replaced with a 0, and the 0 at the end, with a 1.

 def check_status(self):
            status = "-1"
            if self.PIN_FILAMENT != -1:
                    status = "0" if GPIO.input(self.PIN_FILAMENT) else "1"
            return jsonify( status = status )

Next move to the function called setup_gpio and change GPIO.FALLING to GPIO.RISING :

  def setup_gpio(self):
            if self.PIN_FILAMENT != -1:
                    GPIO.add_event_detect(self.PIN_FILAMENT, GPIO.RISING, callback=self.check_gpio, bouncetime=self.BOUNCE) 

The last function check_gpio will have the ‘not’ removed from the argument “if not state:”.

 def check_gpio(self, channel):
            state = GPIO.input(self.PIN_FILAMENT)
            self._logger.debug("Detected sensor [%s] state [%s]? !"%(channel, state))
            if state: #safety pin ?
                    self._logger.debug("Sensor [%s]!"%state)
                    if self._printer.is_printing():

Run a test print and see if the printer automatically pauses when the filament is removed. It may take a couple seconds for it to stop after the pause command is sent. you can open the Octoprint log and see the following:

2019-01-07 20:58:42,390 - octoprint.plugins.filament - DEBUG - Detected sensor [17] state [0]? !
2019-01-07 20:59:36,228 - octoprint.util.comm - INFO - Changing monitoring state from "Paused" to "Resuming"
2019-01-07 20:59:36,247 - octoprint.util.comm - INFO - Changing monitoring state from "Resuming" to "Printing"
2019-01-07 20:59:48,483 - octoprint.plugins.filament - DEBUG - Detected sensor [17] state [1]? !
2019-01-07 20:59:48,484 - octoprint.plugins.filament - DEBUG - Sensor [1]!
2019-01-07 20:59:48,486 - octoprint.util.comm - INFO - Changing monitoring state from "Printing" to "Pausing"
2019-01-07 20:59:54,556 - octoprint.util.comm - INFO - Changing monitoring state from "Pausing" to "Paused"

This plugin and sensor are now fully functional and ready for use.