Add bowden length runtime detection and tuning

Still, unit tests must check this thoroughly as this code
is extremely hard to test on the real HW.
pull/141/head
D.R.racer 2021-11-23 11:57:07 +01:00
parent 860b91e42b
commit ecd63b6138
8 changed files with 107 additions and 62 deletions

View File

@ -18,7 +18,10 @@
/// Wrangler for assorted compile-time configuration and constants.
namespace config {
static constexpr const uint8_t toolCount = 5U; ///< Max number of extruders/tools/slots
/// Max number of extruders/tools/slots
/// Beware - if you change this, the EEPROM structure will become invalid and no migration procedures have been implemented.
/// So if you really have to change this, erase your EEPROM content then.
static constexpr const uint8_t toolCount = 5U;
static_assert(toolCount < 15, "Up to 14 valid slots (+1 parking) is supported in EEPROM storage");
// Printer's filament sensor setup
@ -84,9 +87,9 @@ static constexpr U_mm couplerToBowden = 3.5_mm; /// 3.5_mm /// FINDA Coupler scr
// just another piece of PLA (probably having more resistance in the tubes)
// and we are at least 40mm off! It looks like this really depends on the exact position
// We'll probably need to check for stallguard while pushing the filament to avoid ginding the filament
static constexpr U_mm defaultBowdenLength = 427.0_mm; ///< ~427.0_mm - Default Bowden length. @TODO Should be stored in EEPROM. 392 a 784
static constexpr U_mm minimumBowdenLength = 341.0_mm; ///< ~341.0_mm - Minimum bowden length. @TODO Should be stored in EEPROM.
static constexpr U_mm maximumBowdenLength = 792.0_mm; ///< ~792.0_mm - Maximum bowden length. @TODO Should be stored in EEPROM.
static constexpr U_mm defaultBowdenLength = 427.0_mm; /// ~427.0_mm /// Default Bowden length.
static constexpr U_mm minimumBowdenLength = 341.0_mm; /// ~341.0_mm /// Minimum bowden length.
static constexpr U_mm maximumBowdenLength = 792.0_mm; /// ~792.0_mm /// Maximum bowden length.
static constexpr U_mm feedToFinda = cuttingEdgeToFindaMidpoint + filamentMinLoadedToMMU;
static constexpr U_mm maximumFeedToFinda = feedToFinda + 20.0_mm; ///< allow for some safety margin to load to FINDA
static constexpr U_mm pulleyHelperMove = 10.0_mm; ///< Helper move for Load/Unload error states - when the MMU should slowly move the filament a bit

View File

@ -15,5 +15,13 @@ void EEPROM::UpdateByte(EEPROM::addr_t addr, uint8_t value) {
eeprom_update_byte((uint8_t *)addr, value);
}
uint16_t EEPROM::ReadWord(EEPROM::addr_t addr) {
return eeprom_read_word((const uint16_t *)addr);
}
void EEPROM::UpdateWord(EEPROM::addr_t addr, uint16_t value) {
eeprom_update_word((uint16_t *)addr, value);
}
} // namespace eeprom
} // namespace hal

View File

@ -28,6 +28,31 @@ void logic::FeedToBondtech::GoToPushToNozzle() {
state = PushingFilamentIntoNozzle;
}
void FeedToBondtech::UpdateBowdenLength(int32_t feedEnd_mm) {
int32_t measuredBowdenLength = abs(feedEnd_mm - feedStart_mm);
if (measuredBowdenLength < config::maximumBowdenLength.v) { // is the measured length any valid/acceptable?
static_assert(config::maximumBowdenLength.v <= 65535, "Max bowden length too long");
int16_t mbl = (int16_t)measuredBowdenLength;
int16_t difference = abs(mbl - mps::BowdenLength::Get(mg::globals.ActiveSlot()));
if (difference > 5) { // @@TODO 5_mm is it good enough?
mps::BowdenLength::Set(mg::globals.ActiveSlot(), mbl);
}
}
}
bool FeedToBondtech::PushingFilament() {
if (mfs::fsensor.Pressed()) {
mm::motion.AbortPlannedMoves(); // stop pushing filament
GoToPushToNozzle();
} else if (mm::motion.StallGuard(mm::Pulley)) {
// stall guard occurred during movement - the filament got stuck
state = Failed; // @@TODO may be even report why it failed
} else if (mm::motion.QueueEmpty()) {
return false;
}
return true;
}
bool FeedToBondtech::Step() {
switch (state) {
case EngagingIdler:
@ -67,6 +92,30 @@ bool FeedToBondtech::Step() {
// // stall guard occurred during movement - the filament got stuck
// state = PulleyStalled;
} else if (mm::motion.QueueEmpty()) { // all moves have been finished and the fsensor didn't switch on
/*=======
dbg_logic_fP(PSTR("Pulley start steps %u"), mm::motion.CurPosition(mm::Pulley));
state = PushingFilamentToFSensorFast;
mm::motion.InitAxis(mm::Pulley);
feedStart_mm = mm::stepsToUnit<mm::P_pos_t>(mm::P_pos_t({ mm::motion.CurPosition(mm::Pulley) }));
// fast feed in millimeters - if the EEPROM value is incorrect, we'll get the default length
mm::motion.PlanMove<mm::Pulley>(
{ (long double)mps::BowdenLength::Get(mg::globals.ActiveSlot()) },
config::pulleyFeedrate, config::pulleySlowFeedrate);
}
return false;
case PushingFilamentToFSensorFast:
if (!PushingFilament()) { // ran out of stored bowden length, continue slowly
state = PushingFilamentToFSensorSlow;
// do the remaining move up to maximum bowden length slowly
mm::motion.PlanMove<mm::Pulley>(
{ (long double)abs(config::maximumBowdenLength.v - mps::BowdenLength::Get(mg::globals.ActiveSlot())) }, // fast feed in millimeters - if the EEPROM value is incorrect, we'll
config::pulleySlowFeedrate, config::pulleySlowFeedrate);
}
return false;
case PushingFilamentToFSensorSlow:
if (!PushingFilament()) { // all moves have been finished and the fsensor didn't switch on
>>>>>>> Add bowden length runtime detection and tuning
*/
state = Failed;
}
return false;
@ -92,6 +141,9 @@ bool FeedToBondtech::Step() {
dbg_logic_P(PSTR("Feed to Bondtech --> Idler disengaged"));
dbg_logic_fP(PSTR("Pulley end steps %u"), mpu::pulley.CurrentPosition_mm());
state = OK;
mpu::pulley.Disable();
UpdateBowdenLength(mm::stepsToUnit<mm::P_pos_t>(mm::P_pos_t({ mm::motion.CurPosition(mm::Pulley) })));
mm::motion.Disable(mm::Pulley);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
}
return false;

View File

@ -1,6 +1,7 @@
/// @file feed_to_bondtech.h
#pragma once
#include <stdint.h>
#include "../modules/axisunit.h"
namespace logic {
@ -10,6 +11,10 @@ namespace logic {
/// Then it feeds a bit more very gently to push the filament into the nozzle
/// Disengages the Idler after finishing the feed.
/// Disables the Pulley axis after disengaging the idler.
///
/// If filament has been successfully fed into the fsensor,
/// records/updates PTFE length for the active slot.
/// To prevent constant EEPROM updates only significant changes are recorded.
struct FeedToBondtech {
/// internal states of the state machine
enum {
@ -46,8 +51,15 @@ struct FeedToBondtech {
void GoToPushToNozzle();
private:
/// Update bowden length if changed significantly
void UpdateBowdenLength(int32_t feedEnd_mm);
/// Common processing of pushing filament into fsensor (reused by multiple states)
bool PushingFilament();
uint8_t state;
uint8_t maxRetries;
int32_t feedStart_mm; // intentionally trying to avoid using U_mm because it is a float (reps. long double)
};
} // namespace logic

View File

@ -145,6 +145,13 @@ static constexpr typename AU::type_t unitToSteps(U v) {
return unitToAxisUnit<AU>(v).v;
}
/// Convert a steps type (pos_t or steps_t) to a unit::Unit.
/// Extract the raw step count from an AxisUnit with type checking.
template <typename U, typename AU>
static constexpr typename U::type_t stepsToUnit(AU pos) {
return axisUnitToUnit<U, AU>(pos);
}
// Pulley
typedef AxisUnit<pos_t, Pulley, Lenght> P_pos_t; ///< Pulley position type (steps)
typedef AxisUnit<steps_t, Pulley, Speed> P_speed_t; ///< Pulley speed type (steps/s)

View File

@ -3,6 +3,7 @@
#include "../hal/eeprom.h"
#include "globals.h"
#include "../config/config.h"
#include "axisunit.h"
#include <stddef.h>
@ -43,10 +44,14 @@ static const uint8_t layoutVersion = 0xff;
// ideally, this would have been a nice constexpr (since it is a compile time constant), but the C++ standard prohibits reinterpret_casts in constexpr
static eeprom_t *const eepromBase = reinterpret_cast<eeprom_t *>(0); ///< First EEPROM address
constexpr const uint16_t eepromEmpty = 0xffffU; ///< EEPROM content when erased
constexpr const uint16_t eepromLengthCorrectionBase = 7900U; ///< legacy bowden length correction base (~391mm)
constexpr const uint16_t eepromBowdenLenDefault = 8900U; ///< Default bowden length (~427 mm)
constexpr const uint16_t eepromBowdenLenMinimum = 6900U; ///< Minimum bowden length (~341 mm)
constexpr const uint16_t eepromBowdenLenMaximum = 16000U; ///< Maximum bowden length (~792 mm)
namespace mm = modules::motion;
constexpr const uint16_t eepromLengthCorrectionBase = config::defaultBowdenLength.v;
constexpr const uint16_t eepromBowdenLenDefault = config::defaultBowdenLength.v; ///< Default bowden length (~427 mm)
constexpr const uint16_t eepromBowdenLenMinimum = config::minimumBowdenLength.v; ///< Minimum bowden length (~341 mm)
constexpr const uint16_t eepromBowdenLenMaximum = config::maximumBowdenLength.v; ///< Maximum bowden length (~792 mm)
// static_assert (mm::unitToSteps<mm::P_pos_t>(config::maximumBowdenLength) < 65535U, "");
namespace ee = hal::eeprom;
@ -88,11 +93,10 @@ static bool validBowdenLen(const uint16_t BowdenLength) {
///
/// Returns stored value, doesn't return actual value when it is edited by increase() / decrease() unless it is stored.
/// @return stored bowden length
uint16_t BowdenLength::get() {
uint8_t filament = mg::globals.ActiveSlot();
if (validFilament(filament)) {
uint16_t BowdenLength::Get(uint8_t slot) {
if (validFilament(slot)) {
// @@TODO these reinterpret_cast expressions look horrible but I'm keeping them almost intact to respect the original code from MM_control_01
uint16_t bowdenLength = ee::EEPROM::ReadByte(reinterpret_cast<size_t>(&(eepromBase->eepromBowdenLen[filament])));
uint16_t bowdenLength = ee::EEPROM::ReadByte(reinterpret_cast<size_t>(&(eepromBase->eepromBowdenLen[slot])));
if (eepromEmpty == bowdenLength) {
const uint8_t LengthCorrectionLegacy = ee::EEPROM::ReadByte(reinterpret_cast<size_t>(&(eepromBase->eepromLengthCorrection)));
@ -107,46 +111,10 @@ uint16_t BowdenLength::get() {
return eepromBowdenLenDefault;
}
/// @brief Construct BowdenLength object which allows bowden length manipulation
///
/// To be created on stack, new value is permanently stored when object goes out of scope.
/// Active filament and associated bowden length is stored in member variables.
BowdenLength::BowdenLength()
: filament(mg::globals.ActiveSlot()) // @@TODO - verify correct initialization order
, length(BowdenLength::get()) // @@TODO
{
}
/// @brief Increase bowden length
///
/// New value is not stored immediately. See ~BowdenLength() for storing permanently.
/// @retval true passed
/// @retval false failed, it is not possible to increase, new bowden length would be out of range
bool BowdenLength::increase() {
if (validBowdenLen(length + stepSize)) {
length += stepSize;
return true;
void BowdenLength::Set(uint8_t slot, uint16_t steps) {
if (validFilament(slot)) {
ee::EEPROM::UpdateWord(EEOFFSET(eepromBase->eepromBowdenLen[slot]), steps);
}
return false;
}
/// @brief Decrease bowden length
///
/// New value is not stored immediately. See ~BowdenLength() for storing permanently.
/// @retval true passed
/// @retval false failed, it is not possible to decrease, new bowden length would be out of range
bool BowdenLength::decrease() {
if (validBowdenLen(length - stepSize)) {
length -= stepSize;
return true;
}
return false;
}
/// @brief Store bowden length permanently.
BowdenLength::~BowdenLength() {
if (validFilament(filament))
ee::EEPROM::UpdateWord(EEOFFSET(eepromBase->eepromBowdenLen[filament]), length);
}
/// @brief Get filament storage status

View File

@ -16,22 +16,17 @@ void Init();
/// Erase the whole EEPROM
void EraseAll();
/// @brief Read manipulate and store bowden length
/// @brief Read and store bowden length
///
/// Value is stored independently for each filament.
/// Active filament is deduced from active_extruder global variable.
class BowdenLength {
public:
static uint16_t get();
static const uint8_t stepSize = 10u; ///< increase()/decrease() bowden length step size
BowdenLength();
bool increase();
bool decrease();
~BowdenLength();
/// @returns bowden length for selected slot
static uint16_t Get(uint8_t slot);
private:
uint8_t filament; ///< Selected filament
uint16_t length; ///< Selected filament bowden length
/// Sets
static void Set(uint8_t slot, uint16_t steps);
};
/// @brief Read and store last filament loaded to nozzle

View File

@ -71,7 +71,7 @@ TEST_CASE("feed_to_bondtech::feed_phase_unlimited", "[feed_to_bondtech]") {
if( step == 100 ){
mfs::fsensor.ProcessMessage(true);
}
return fb.State() == FeedToBondtech::PushingFilamentToFSensor; },
return fb.State() == FeedToBondtech::PushingFilamentToFSensorFast; },
1500));
REQUIRE(mfs::fsensor.Pressed());