Welcome! Log In Create A New Profile

Advanced

Adding optoencoder to endstop slot to provide filament odometer - firmware help needed

Posted by dgm3333 
Adding optoencoder to endstop slot to provide filament odometer - firmware help needed
September 12, 2018 01:13AM
I'm trying to figure out how to add a filament travel monitor to (hopefully) provide early warning of filament slippages.
I use Ramps and have 3 free endstop slots so I want to repurpose one to work as an encoder to count toggles (change from open to closed) to provide the filament travel detector.

Background follows, but any help for the following would be appreciated:
Steps:
1. repurpose the endstop slot to count toggles
2. every time the internal marlin odometer gained another 5cm (?) then check the encoder count is in the right ballpark (eg 49-51 toggles).
Obviously 2x retract distance would have to be subtracted from the count as the encoder will count upwards with filament movement in either direction.
3. sound an alarm/trigger M300 code if the values in 2 differ significantly.
3a. send the ratio to the LCD with M117 [string]
4. reset the toggle count
5. repeat endlessly...

There are a number of posts on the web by people who claim to have added filament odometer which "works with Marlin", but none include instructions and there doesn't seem to be a g-code relevant (just the M600 runout code).
I'm thus a bit suspicious no-one has *actually* done it in Marlin.

I have printed the physical parts for a simple filament odometer using an optoendstop and an encoder wheel which just rolls along the filament.
The wheel has 1 "slot" per mm, and is printed in clear filament.
I accidentally printed the wheel at too high a temperature which meant the slots melted, so the optoendstop reads "open" when shining through the clear plastic and "closed" when shining through the opaque "gap" but otherwise works perfectly.
I coated the edge of the wheel with gorilla glue (essentially PVA) to increase grip where it contacts the filament, and it seems to turn reasonably accurately.

As above I'd like to trigger the buzzer on a 12864 LCD Controller if the measured travel distance is signficantly different to the expected distance (as commanded by G1 extrusion)
Icing on the cake would also be to output to the LCD the odometer as well as measured:commanded travel ratio

Hoping someone can help.
Tx
David

Edited 2 time(s). Last edit at 09/12/2018 01:19AM by dgm3333.


Setup: Prusa/Ramps 1.4 running Marlin v1.1.9 firmware. Hosted by XenialPup64 7.5 (Linux) running Repetier-host v2.1.3. Slicing: Cura. Design: OpenSCAD 2018.09.05, Blender, Inkscape. Mesh Repair: Meshlab, Netfab Basic.
Re: Adding optoencoder to endstop slot to provide filament odometer - firmware help needed
September 19, 2018 05:24PM
Still hoping someone with some knowledge of the code might help here...


Never having looked at Marlin internals before I don't really have any clue what I'm doing, so more likely to break something than not (and given the time cost of each build/test cycle and the relative lack of interest in this this post it probably isn't worth pursuing, but these seem like potential points for code to enable the above steps.
I'm acually pretty suspicious I must be missing something, because on the face of it this seems such a basic enhancement, I would have thought it would have been done long ago.

I think also it would make more sense initially to use gcode_M118() [Marlin_main.cpp 8966] to echo a message to the host initially for testing then add the M117 feature later


Setting up a pin for the trigger (I use RAMPS, so everything refers to pins_RAMPS.h) I think initially I'd just hijack one of the runout pins
#ifndef FIL_RUNOUT_PIN
#define FIL_RUNOUT_PIN 19 // This co-opts pin used by #define Z_MAX_PIN 19
#endif

if you already have a runout system in place you could use one of the other 4 in runout.h
#if NUM_RUNOUT_SENSORS > 4
case 4: is_out = READ(FIL_RUNOUT5_PIN) == FIL_RUNOUT_INVERTING; break;
#endif

Would it be reasonable to modify near the following code in runout.h (which is already being called by the runout pin interrupt) to increment the odometer?
Need to check if the runout interrupt is triggers by both rising and falling signal (which would be required by and encoder wheel). Also I'm not sure how Marlin currently compensates for jitter on the endstops, as this is very likely to be an issue with the wheel too.
Also the prepare_move_to_destination() bit code would have to set a flag if it's a retract (in which case the odometer would have to decrement)

inline void gcode_G0_G1(
...
FORCE_INLINE static void run() {
if ((IS_SD_PRINTING || print_job_timer.isRunning()) && check() && !filament_ran_out) {
filament_ran_out = true;
enqueue_and_echo_commands_P(PSTR(FILAMENT_RUNOUT_SCRIPT));
planner.synchronize();
}
}

The looks like it probably refers to the the extruder current position (in Marlin_main.cpp 13651)
This would seem Iike a reasonable place to check that the filament odometer value is at the same point as the current position and set the retract flag for so the odometer was correctly incremented/decremented.
The alarm (step 3) looks like it could be triggered from anywhere with BUZZ(duration, frequency), so this section could sound this if there was a problem and somehow (?) queue the M118 message for the host;

void prepare_move_to_destination() {
...
if (!DEBUGGING(DRYRUN)) {
if (destination[E_AXIS] != current_position[E_AXIS]) {
#if ENABLED(PREVENT_COLD_EXTRUSION)


(obviously providing HAS_BUZZER == true which seems to be set if you have #define BEEPER_PIN in pins_RAMPS.h - this seems to be under LCD panel code, but there seems no reason a piezo couldn't be hooked up to any aux pin, so you could put this with #define SPEAKER in Configuration.h
[I'm not sure the difference between BUZZER and SPEAKER. the latter only seems to be used in buzzer.h for this code
#if ENABLED(SPEAKER)
CRITICAL_SECTION_START;
::tone(BEEPER_PIN, this->state.tone.frequency, this->state.tone.duration);
CRITICAL_SECTION_END;


Setup: Prusa/Ramps 1.4 running Marlin v1.1.9 firmware. Hosted by XenialPup64 7.5 (Linux) running Repetier-host v2.1.3. Slicing: Cura. Design: OpenSCAD 2018.09.05, Blender, Inkscape. Mesh Repair: Meshlab, Netfab Basic.
Here are my latest workings - I haven't tried running it yet




//If this actually works it will need to be modified to enable one odometer per filament input (for the same reason up to 5 runout sensors can be installed)
// I haven't yet, as it should be a simple matter of converting each variable to an array.
// Also I'm not sure how each pin will end up being configured

/**
* Filament Odometer Sensors
* Mechanical or opto encoders are used to check for the movement of filament.
*
* RAMPS-based boards use ZMAX_PIN for the first runout sensor.
* For other boards you may need to define FIL_RUNOUT_PIN, FIL_RUNOUT2_PIN, etc.
* By default the firmware assumes HIGH=FILAMENT PRESENT.
*/
//#define FILAMENT_ODOMETER_SENSOR
#if ENABLED(FILAMENT_ODOMETER_SENSOR)
#define NUM_ODOMETER_SENSORS 1 // Number of sensors, up to one per extruder. Define a FIL_RUNOUT#_PIN for each.
#define ODOMETER_MM_PER_TICK 1 // number of mm travelled for each switch of the odometer (this is a float)
#define ODOMETER_ERROR_PERCENT 5 // percentage allowable error in odometer comparison with expected extruder value
#define FILAMENT_ODOMETER_MISMATCH_SCRIPT "M600"
#endif

//Could move this to appropriate pins_XXX.h (eg pins_RAMPS.h) when working correctly
#ifndef FIL_ODOMETER_PIN
#define FIL_ODOMETER_PIN 19 // This co-opts pin used by #define Z_MAX_PIN 19
#endif


//pin error checking in SanityCheck.h
// may or may not be needed - but sensible to at least check appropriate number of pins are defined...



//don't need this currently (from Marlin_main.cpp) - would put below: #if ENABLED(FILAMENT_RUNOUT_SENSOR)
#if ENABLED(FILAMENT_ODOMETER_SENSOR)
#include "odometer.h"
#endif


// for language.h
#define MSG_FILAMENT_ODOMETER_SENSOR_STATE "odometer state: "
#define MSG_FILAMENT_ODOMETER_SENSOR_COUNT "odometer count: "
#define MSG_FILAMENT_ODOMETER_SENSOR_VALUE "odometer value: "


// Seems sensible to check for change of odometer state when the processor isn't busy:-
//void manage_inactivity(const bool ignore_stepper_queue/*=false*/) {
#if ENABLED(FILAMENT_ODOMETER_SENSOR)
odometer.run();
//odometer.reset(); not sure how often this should be reset
#endif

// Hopefully the above inactivity code will be cycled at least once per odometer tick
// (which might not be true if a very accurate odometer is chosen)
// if not the ISR might have to be used
// However the RUNOUT doesn't use an ISR, so hopefully all will be OK

//not sure where this is defined:
// #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
// setup_endstop_interrupts();
// #endif

//// One ISR for all EXT-Interrupts (defined in endstop_interrupts.h)
//void endstop_ISR(void) { endstops.update(); }
// setup in
//void setup_endstop_interrupts( void ) {







//state of endstops and filament runout is reported in endstops.cpp, so would make sense to add this here
//void Endstops::M119() {

#if ENABLED(FILAMENT_ODOMETER_SENSOR)
// perhaps not ideal as will update for each call to odometer
SERIAL_PROTOCOLPGM(MSG_FILAMENT_ODOMETER_SENSOR_STATE);
SERIAL_PROTOCOLLN(odometer.state());
SERIAL_PROTOCOLPGM(MSG_FILAMENT_ODOMETER_SENSOR_COUNT);
SERIAL_PROTOCOLLN(odometer.count());
SERIAL_PROTOCOLPGM(MSG_FILAMENT_ODOMETER_SENSOR_VALUE);
SERIAL_PROTOCOLLN(odometer.value());
#endif




#ifndef _ODOMETER_H_
#define _ODOMETER_H_
// copied and modified from runout.h

// Not sure which of these is actually required without looking through them, so keep them all
#include "cardreader.h"
#include "printcounter.h"
#include "stepper.h"
#include "Marlin.h"

#include "MarlinConfig.h"

class FilamentOdometerSensor {
public:
FilamentOdometerSensor() {}
//most of these functions could be called from multiple places, so FORCE_INLINE removed

// in the Marlin code this is defined in runout.cpp, not runout.h (not sure why)
// can't think of any reason why a pullup would be required, so no option included
static void setup() { SET_INPUT(FIL_ODOMETER_PIN); }


// not totally sure current_position[E_AXIS] is the true position - I think this is actually only for the planner
// however it's used for serial output in void report_current_position() so seems reasonable for a first guess
// this is another candidate: planner.get_axis_position_mm(E_AXIS)
// NB this is defined in planner.cpp as float Planner::get_axis_position_mm(const AxisEnum axis) and returns (for non-CORE printers)
// stepper.position(axis) * steps_to_mm[axis];
static void reset() { odometer_state = READ(FIL_ODOMETER_PIN); odometer_count = 0; last_extruder_value = current_position[E_AXIS]; }

static bool state() { update(); return odometer_state; }

static long count() { update(); return odometer_count; }

static long value() { update(); return odometer_count * ODOMETER_MM_PER_TICK; }

static void update() {
if (READ(FIL_ODOMETER_PIN) != odometer_state) {
odometer_state = !odometer_state;
// Need to figure out how to tell if filament is extruding or retracting
// might be something to do with the FWRETRACT definition in the void gcode_G0_G1 function in Marlin_main.cpp
//filamentextruding ? odometer_count++ : odometer_count;
odometer_count++;
}
}

static boolean check() {
update();
// Need to check units are comparable
// currently odometer is just counting state flips, not converting to a distance using ODOMETER_MM_PER_TICK

odometer_allowable_error = odometer_count * ODOMETER_ERROR_PERCENT / 100
if (odometer_count + odometer_allowable_error > current_position[E_AXIS] - last_extruder_value &&
odometer_count - odometer_allowable_error < current_position[E_AXIS] - last_extruder_value) {
return true;
} else {
return false;
}

}

FORCE_INLINE static void run() {
if ((IS_SD_PRINTING || print_job_timer.isRunning()) && check() && !odometer_mismatch) {
odometer_mismatch = true;
enqueue_and_echo_commands_P(PSTR(ODOMETER_MISMATCH_SCRIPT));
planner.synchronize();
}
}

private:
static bool odometer_state;
statit bool odometer_mismatch;
static float odometer_count; // an odometer with 1mm/state change and uint16 could only measure 13m
// however that migh probably be OK as this should be reset
// however using a float to allow option of very fine odometer readings
// and it's possible to retract the filament after a reset, so may need
// to be negative
static long last_extruder_value;
long odometer_allowable_error;

};

FilamentOdometerSensor odometer; // if in .h, this will need to be extern

#endif // _ODOMETER_H_

Edited 2 time(s). Last edit at 10/01/2018 06:22PM by dgm3333.


Setup: Prusa/Ramps 1.4 running Marlin v1.1.9 firmware. Hosted by XenialPup64 7.5 (Linux) running Repetier-host v2.1.3. Slicing: Cura. Design: OpenSCAD 2018.09.05, Blender, Inkscape. Mesh Repair: Meshlab, Netfab Basic.
Sorry, only registered users may post in this forum.

Click here to login