Skip to content
This repository was archived by the owner on Dec 19, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ After installation a restart of Octoprint is recommended.
* In [BigTreeTech SmartFilamentSensor Manual](https://github.com/bigtreetech/smart-filament-detection-module/tree/master/manual) on page 12 you can find the functionality of the pins. Please ensure that there is no undocumented twist in your cable
* My recommended GPIO pins: 11, 13, 15, 17 (such without any special usage). Please check the [documentation](https://www.raspberrypi.org/documentation/usage/gpio/) of your Raspberry Pi version/model. Also other pins could work, if you know how to configure it on the Raspberry, but it might be tricky and not work out of the box.

Note: The BTT Pins are labeled as follows

S for SIN <--- signal line (i.e. data source--attach to chosen GPIO pin)
G for GND <--- This is ground
V for VDD <--- +3.3v in

**Attention**
There are two different modes for GPIO pins:
* BCM (Broadcom SOC channel) - the numbers after the GPIO label
Expand Down Expand Up @@ -93,5 +99,8 @@ G1 Z2.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed
## Outlook
Support of multiple sensors for multiextruders like 4 channel kraken hotend

## News
Development and support pause till summer 2021 due to a lack of time

## Contact
[![PayPal](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=AHS3MUTFXXMNG "Donate for Octoprint Smart-Filament-Sensor Plugin")
[![PayPal](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=AHS3MUTFXXMNG "Donate for Octoprint Smart-Filament-Sensor Plugin")
151 changes: 143 additions & 8 deletions octoprint_smart_filament_sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import RPi.GPIO as GPIO
from time import sleep
import flask
from flask import jsonify
import json
from octoprint_smart_filament_sensor.filament_motion_sensor_timeout_detection import FilamentMotionSensorTimeoutDetection
from octoprint_smart_filament_sensor.data import SmartFilamentSensorDetectionData
Expand All @@ -23,11 +24,14 @@ def initialize(self):
GPIO.setwarnings(False) # Disable GPIO warnings

self.print_started = False
self.preLastE = -1
self.lastE = -1
self.currentE = -1
self.START_DISTANCE_OFFSET = 7
self.send_code = False
self._data = SmartFilamentSensorDetectionData(self.motion_sensor_detection_distance, True, self.updateToUi)
self.lastEStateChange = -1 #not set
self.previousState = -1 #not set

#Properties
@property
Expand Down Expand Up @@ -69,6 +73,33 @@ def mode(self):
#def send_gcode_only_once(self):
# return self._settings.get_boolean(["send_gcode_only_once"])

#Physical distance detection
@property
def motion_sensor_physical_distance(self):
dist = float(self._settings.get(["motion_sensor_physical_distance"]))
return dist

@property
def motion_sensor_physical_distance_low(self):
return float(self._settings.get(["motion_sensor_physical_distance_low"]))

@property
def motion_sensor_physical_distance_high(self):
return float(self._settings.get(["motion_sensor_physical_distance_high"]))

@property
def motion_sensor_physical_distance_tolerance(self):
return float(self._settings.get(["motion_sensor_physical_distance_tolerance"]))

@property
def motion_sensor_retraction_distance(self):
return float(self._settings.get(["motion_sensor_retraction_distance"]))

@property
def motion_sensor_total_distance_detection(self):
return float(self._settings.get(["motion_sensor_total_distance_detection"]))


# Initialization methods
def _setup_sensor(self):
# Clean up before intializing again, because ports could already be in use
Expand All @@ -81,10 +112,10 @@ def _setup_sensor(self):
self._logger.info("Using BCM Mode")
GPIO.setmode(GPIO.BCM)

GPIO.setup(self.motion_sensor_pin, GPIO.IN)
GPIO.setup(self.motion_sensor_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Add reset_distance if detection_method is distance_detection
if (self.detection_method == 1):
if (self.detection_method == 1 or self.detection_method == 2):
# Remove event first, because it might been in use already
try:
GPIO.remove_event_detect(self.motion_sensor_pin)
Expand Down Expand Up @@ -115,7 +146,7 @@ def get_settings_defaults(self):
mode=0, # Board Mode
motion_sensor_enabled = True, #Sensor detection is enabled by default
motion_sensor_pin=-1, # Default is no pin
detection_method = 0, # 0 = timeout detection, 1 = distance detection
detection_method = 0, # 0 = timeout detection, 1 = distance detection, 2 = distance detection 2

# Distance detection
motion_sensor_detection_distance = 15, # Recommended detection distance from Marlin would be 7
Expand All @@ -124,6 +155,14 @@ def get_settings_defaults(self):
motion_sensor_max_not_moving=45, # Maximum time no movement is detected - default continously
pause_command="M600",
#send_gcode_only_once=False, # Default set to False for backward compatibility

# For distance detection 2
motion_sensor_physical_distance = -1,
motion_sensor_physical_distance_low = -1,
motion_sensor_physical_distance_high = -1,
motion_sensor_physical_distance_tolerance = 10.0,
motion_sensor_retraction_distance = 6.0,
motion_sensor_total_distance_detection = False
)

def on_settings_save(self, data):
Expand Down Expand Up @@ -156,6 +195,53 @@ def start_connection_test(self):
self._data.connection_test_running = True
self._logger.info("Connection test started")

# Detection of physical distance
def detect_physical_distance(self):
self._logger.info("Physical distance detection started")
startState = GPIO.input(self.motion_sensor_pin)
extruderValue = 0.00
begin = 1
end = 1
secondChange = False
secondChangeValue = 0
distance = 0
distanceLow = 0
distanceHigh = 0
while True:
extruderValue -= 0.1
self._printer.extrude(-0.1, 200) #relative
sleep(0.5)
state = GPIO.input(self.motion_sensor_pin)
if (state != startState and begin == 1 and not secondChange):
begin = extruderValue
self._logger.info("Step 1, state = "+str(state)+", eV = "+str(extruderValue))
if (state == startState and begin != 1 and not secondChange):
secondChange = True
secondChangeValue = extruderValue
if (state):
distanceLow = begin - extruderValue
else:
distanceHigh = begin - extruderValue
self._logger.info("Step 2, state = "+str(state)+", eV = "+str(extruderValue))
if (state != startState and begin != 1 and secondChange):
end = extruderValue
if (state):
distanceLow = secondChangeValue - extruderValue
else:
distanceHigh = secondChangeValue - extruderValue
distance = begin - end
self._logger.info("Step 3, state = "+str(state)+", eV = "+str(extruderValue))
break
if (extruderValue < -20.00):
self._logger.info("Step 4, state = "+str(state)+", eV = "+str(extruderValue))
break
self._settings.set(["motion_sensor_physical_distance"], round(distance,1))
self._settings.set(["motion_sensor_physical_distance_low"], round(distanceLow,1))
self._settings.set(["motion_sensor_physical_distance_high"], round(distanceHigh,1))
self._settings.save(True)
self._logger.info("Physical distance detection ended. Distance is "+str(distance)+", distance low is "+str(distanceLow)+", distance high is "+str(distanceHigh))


# Starts the motion sensor if the sensors are enabled
def motion_sensor_start(self):
self._logger.debug("Sensor enabled: " + str(self.motion_sensor_enabled))
Expand All @@ -167,6 +253,11 @@ def motion_sensor_start(self):
self._logger.debug("GPIO mode: BCM Mode")
self._logger.debug("GPIO pin: " + str(self.motion_sensor_pin))

# New detection
if (self.detection_method == 2):
self._logger.info("Motion sensor started: New detection")
self._logger.debug("Detection Mode: New detection")

# Distance detection
if (self.detection_method == 1):
self._logger.info("Motion sensor started: Distance detection")
Expand Down Expand Up @@ -216,16 +307,19 @@ def reset_distance (self, pPin):

# Initialize the distance detection values
def init_distance_detection(self):
self.preLastE = float(-1)
self.lastE = float(-1)
self.currentE = float(0)
self.reset_remainin_distance()
self.lastEStateChange = -1 #not set
self.previousState = GPIO.input(self.motion_sensor_pin)

# Reset the remaining distance on start or resume
# START_DISTANCE_OFFSET is used for the (re-)start sequence
def reset_remainin_distance(self):
self._data.remaining_distance = (float(self.motion_sensor_detection_distance) + self.START_DISTANCE_OFFSET)

# Calculate the remaining distance
# Calculate the remaining distance or calculation for detection method 2
def calc_distance(self, pE):
if (self.detection_method == 1):
# Only with absolute extrusion the delta distance must be calculated
Expand Down Expand Up @@ -260,6 +354,39 @@ def calc_distance(self, pE):

else:
self.printer_change_filament()
if (self.detection_method == 2):
# Only with absolute extrusion the delta distance must be calculated
if (self._data.absolut_extrusion):
# LastE is not used and set to the same value as currentE
self.preLastE = self.lastE
if (self.lastE == -1):
self.lastE = pE
else:
self.lastE = self.currentE
self.currentE = pE

self._logger.debug("LastE: " + str(self.lastE) + "; CurrentE: " + str(self.currentE))

# Calculate the remaining distance from detection distance
# currentE - lastE is the delta distance
if(self._data.absolut_extrusion):
deltaDistance = self.lastE - self.preLastE
# With relative extrusion the current extrusion value is the delta distance
else:
deltaDistance = float(self.preLastE)

if (self.preLastE != -1 and deltaDistance > 0 and deltaDistance < self.motion_sensor_retraction_distance):
state = GPIO.input(self.motion_sensor_pin)
rDist = self.motion_sensor_physical_distance_tolerance - (self.lastE-self.lastEStateChange)
if (self.lastEStateChange != -1 and rDist < 0):
self._logger.info("Detection: LastE: "+str(self.lastE)+",deltaE: "+str(deltaDistance)+", state: "+str(state)+", rDist: "+str(rDist))
self.printer_change_filament()


if (self.previousState != state and ((self.motion_sensor_total_distance_detection and state == 1) or not self.motion_sensor_total_distance_detection)):
self.lastEStateChange = self.lastE #lastE is more accurate then currentE
self._logger.info("Change: LastE: "+str(self.lastE)+",deltaE: "+str(deltaDistance)+", state: "+str(state)+", rDist: "+str(rDist))
self.previousState = state

def updateToUi(self):
self._plugin_manager.send_plugin_message(self._identifier, self._data.toJSON())
Expand All @@ -279,7 +406,7 @@ def on_event(self, event, payload):
if event is Events.PRINT_STARTED:
self.stop_connection_test()
self.print_started = True
if(self.detection_method == 1):
if(self.detection_method == 1 or self.detection_method == 2):
self.init_distance_detection()

elif event is Events.PRINT_RESUMED:
Expand Down Expand Up @@ -322,7 +449,8 @@ def on_event(self, event, payload):
def get_api_commands(self):
return dict(
startConnectionTest=[],
stopConnectionTest=[]
stopConnectionTest=[],
detectPhysicalDistance=[]
)

def on_api_command(self, command, data):
Expand All @@ -333,6 +461,13 @@ def on_api_command(self, command, data):
elif(command == "stopConnectionTest"):
self.stop_connection_test()
return flask.make_response("Stopped connection test", 204)
elif(command == "detectPhysicalDistance"):
self.detect_physical_distance()
return jsonify(
physicalDistance=self.motion_sensor_physical_distance,
physicalDistanceLow=self.motion_sensor_physical_distance_low,
physicalDistanceHigh=self.motion_sensor_physical_distance_high,
)
else:
return flask.make_response("Not found", 404)

Expand Down Expand Up @@ -375,7 +510,7 @@ def update_hook(self):
# G0 or G1: Caluclate the remaining distance
def distance_detection(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
# Only performed if distance detection is used
if(self.detection_method == 1 and self.motion_sensor_enabled):
if((self.detection_method == 1 or self.detection_method == 2) and self.motion_sensor_enabled):
# G0 and G1 for linear moves and G2 and G3 for circle movements
if(gcode == "G0" or gcode == "G1" or gcode == "G2" or gcode == "G3"):
commands = cmd.split(" ")
Expand All @@ -388,7 +523,7 @@ def distance_detection(self, comm_instance, phase, cmd, cmd_type, gcode, *args,

# G92 reset extruder
elif(gcode == "G92"):
if(self.detection_method == 1):
if(self.detection_method == 1 or self.detection_method == 2):
self.init_distance_detection()
self._logger.debug("G92: Reset Extruders")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@ $(function(){

self.settingsViewModel = parameters[0];
self.printerStateViewModel = parameters[1];
self.loginStateViewModel = parameters[2];
self.connectionTestDialog = undefined;

self.remainingDistance = ko.observable(undefined);
self.lastMotionDetected = ko.observable(undefined);
self.isFilamentMoving = ko.observable(undefined);
self.isConnectionTestRunning = ko.observable(false);

// https://github.com/jneilliii/OctoPrint-GoogleDriveBackup/blob/master/octoprint_googledrivebackup/static/js/googledrivebackup.js
self.physicalDistance = ko.observable('')
self.physicalDistanceLow = ko.observable('')
self.physicalDistanceHigh = ko.observable('')
self.onBeforeBinding = function() {
self.physicalDistance(self.settingsViewModel.settings.plugins.smartfilamentsensor.motion_sensor_physical_distance());
self.physicalDistanceLow(self.settingsViewModel.settings.plugins.smartfilamentsensor.motion_sensor_physical_distance_low());
self.physicalDistanceHigh(self.settingsViewModel.settings.plugins.smartfilamentsensor.motion_sensor_physical_distance_high());
};

self.onStartup = function() {
self.connectionTestDialog = $("#settings_plugin_smartfilamentsensor_connectiontest");
};
Expand Down Expand Up @@ -68,6 +79,28 @@ $(function(){
self.RestSuccess = function(response){
return;
}

self.enableDetectionPhysicalDistance = ko.pureComputed(function() {
return !self.printerStateViewModel.isBusy();
});

self.detectPhysicalDistance = function(){
$.ajax({
url: API_BASEURL + "plugin/smartfilamentsensor",
type: "POST",
dataType: "json",
data: JSON.stringify({ "command": "detectPhysicalDistance" }),
contentType: "application/json",
success: self.detectPhysicalDistanceSuccess
});
};

self.detectPhysicalDistanceSuccess = function(response){
self.physicalDistance(response.physicalDistance)
self.physicalDistanceLow(response.physicalDistanceLow)
self.physicalDistanceHigh(response.physicalDistanceHigh)
return;
}
}

OCTOPRINT_VIEWMODELS.push({
Expand Down
Loading