From ecd63b61380c3d757d0092dda86e81009fe9dafd Mon Sep 17 00:00:00 2001 From: "D.R.racer" Date: Tue, 23 Nov 2021 11:57:07 +0100 Subject: [PATCH] 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. --- src/config/config.h | 11 ++-- src/hal/avr/eeprom.cpp | 8 +++ src/logic/feed_to_bondtech.cpp | 52 ++++++++++++++++ src/logic/feed_to_bondtech.h | 12 ++++ src/modules/axisunit.h | 7 +++ src/modules/permanent_storage.cpp | 62 +++++-------------- src/modules/permanent_storage.h | 15 ++--- .../test_feed_to_bondtech.cpp | 2 +- 8 files changed, 107 insertions(+), 62 deletions(-) diff --git a/src/config/config.h b/src/config/config.h index cf73064..85ca0b3 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -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 diff --git a/src/hal/avr/eeprom.cpp b/src/hal/avr/eeprom.cpp index 29e9567..ab14861 100644 --- a/src/hal/avr/eeprom.cpp +++ b/src/hal/avr/eeprom.cpp @@ -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 diff --git a/src/logic/feed_to_bondtech.cpp b/src/logic/feed_to_bondtech.cpp index fc740ab..d5bef91 100644 --- a/src/logic/feed_to_bondtech.cpp +++ b/src/logic/feed_to_bondtech.cpp @@ -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::motion.CurPosition(mm::Pulley) })); + // fast feed in millimeters - if the EEPROM value is incorrect, we'll get the default length + mm::motion.PlanMove( + { (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( + { (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::motion.CurPosition(mm::Pulley) }))); + mm::motion.Disable(mm::Pulley); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on); } return false; diff --git a/src/logic/feed_to_bondtech.h b/src/logic/feed_to_bondtech.h index 3f55940..74b907e 100644 --- a/src/logic/feed_to_bondtech.h +++ b/src/logic/feed_to_bondtech.h @@ -1,6 +1,7 @@ /// @file feed_to_bondtech.h #pragma once #include +#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 diff --git a/src/modules/axisunit.h b/src/modules/axisunit.h index ba57611..36f3846 100644 --- a/src/modules/axisunit.h +++ b/src/modules/axisunit.h @@ -145,6 +145,13 @@ static constexpr typename AU::type_t unitToSteps(U v) { return unitToAxisUnit(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 +static constexpr typename U::type_t stepsToUnit(AU pos) { + return axisUnitToUnit(pos); +} + // Pulley typedef AxisUnit P_pos_t; ///< Pulley position type (steps) typedef AxisUnit P_speed_t; ///< Pulley speed type (steps/s) diff --git a/src/modules/permanent_storage.cpp b/src/modules/permanent_storage.cpp index 3cf3247..cdf3479 100644 --- a/src/modules/permanent_storage.cpp +++ b/src/modules/permanent_storage.cpp @@ -3,6 +3,7 @@ #include "../hal/eeprom.h" #include "globals.h" #include "../config/config.h" +#include "axisunit.h" #include @@ -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(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(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(&(eepromBase->eepromBowdenLen[filament]))); + uint16_t bowdenLength = ee::EEPROM::ReadByte(reinterpret_cast(&(eepromBase->eepromBowdenLen[slot]))); if (eepromEmpty == bowdenLength) { const uint8_t LengthCorrectionLegacy = ee::EEPROM::ReadByte(reinterpret_cast(&(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 diff --git a/src/modules/permanent_storage.h b/src/modules/permanent_storage.h index da8004a..5864a6a 100644 --- a/src/modules/permanent_storage.h +++ b/src/modules/permanent_storage.h @@ -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 diff --git a/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp b/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp index 33103ef..481b998 100644 --- a/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp +++ b/tests/unit/logic/feed_to_bondtech/test_feed_to_bondtech.cpp @@ -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());