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_
//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_