Introduce intelligent homing if Idler and Selector
Both movable components now perform homing sequences transparently whenever the logic layer invalidates the homingValid flag. That reflects the fact, that the user may have moved the Idler or Selector while trying to resolve a HW issue with un/loading filament. Basic rules: - Idler gets rehomed immediately and then moves into the target slot position - Selector rehomes once it is possible - i.e. when filament load state is AtPulley - then it immediately and spontanneously executes the homing sequence and then returns to the desired state Motivation: - resolve startup issues (EEPROM says we have filament, but FINDA is not triggered) - resolve accidental moves of Idler and/or Selector while digging out stuck filament from the unitpull/148/head
parent
7d423df583
commit
6973dbff13
|
|
@ -1,6 +1,8 @@
|
||||||
/// @file command_base.cpp
|
/// @file command_base.cpp
|
||||||
#include "command_base.h"
|
#include "command_base.h"
|
||||||
#include "../modules/globals.h"
|
#include "../modules/globals.h"
|
||||||
|
#include "../modules/finda.h"
|
||||||
|
#include "../modules/fsensor.h"
|
||||||
#include "../modules/idler.h"
|
#include "../modules/idler.h"
|
||||||
#include "../modules/selector.h"
|
#include "../modules/selector.h"
|
||||||
#include "../modules/motion.h"
|
#include "../modules/motion.h"
|
||||||
|
|
@ -89,6 +91,31 @@ void CommandBase::Panic(ErrorCode ec) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandBase::InvalidateHoming() {
|
||||||
|
mi::idler.InvalidateHoming();
|
||||||
|
ms::selector.InvalidateHoming();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBase::InvalidateHomingAndFilamentState() {
|
||||||
|
InvalidateHoming();
|
||||||
|
|
||||||
|
// reset the filament presence according to available sensor states
|
||||||
|
bool fs = mfs::fsensor.Pressed();
|
||||||
|
bool fi = mf::finda.Pressed();
|
||||||
|
|
||||||
|
if (fs && fi) {
|
||||||
|
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::InNozzle);
|
||||||
|
} else if (!fs && fi) {
|
||||||
|
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::InSelector);
|
||||||
|
} else if (!fs && !fi) {
|
||||||
|
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::AtPulley);
|
||||||
|
} else {
|
||||||
|
// we can't say for sure - definitely an error in sensors or something is blocking them
|
||||||
|
// let's assume there is a piece of filament present in the selector
|
||||||
|
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::InSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CommandBase::CheckToolIndex(uint8_t index) {
|
bool CommandBase::CheckToolIndex(uint8_t index) {
|
||||||
if (index >= config::toolCount) {
|
if (index >= config::toolCount) {
|
||||||
error = ErrorCode::INVALID_TOOL;
|
error = ErrorCode::INVALID_TOOL;
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,13 @@ public:
|
||||||
/// The only way out is to reset the board.
|
/// The only way out is to reset the board.
|
||||||
void Panic(ErrorCode ec);
|
void Panic(ErrorCode ec);
|
||||||
|
|
||||||
|
/// Invalidates homing state on Idler and Selector - doesn't change anything about filament load status
|
||||||
|
static void InvalidateHoming();
|
||||||
|
|
||||||
|
/// Invalidates homing state on Idler and Selector + resets the knowledge about
|
||||||
|
/// filament presence according to known sensors (FINDA+FSensor)
|
||||||
|
static void InvalidateHomingAndFilamentState();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// @returns true if the slot/tool index is within specified range (0 - config::toolCount)
|
/// @returns true if the slot/tool index is within specified range (0 - config::toolCount)
|
||||||
/// If not, it returns false and sets the error to ErrorCode::INVALID_TOOL
|
/// If not, it returns false and sets the error to ErrorCode::INVALID_TOOL
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,10 @@ bool LoadFilament::StepInner() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ProgressCode::DisengagingIdler:
|
case ProgressCode::DisengagingIdler:
|
||||||
if (!mi::idler.Engaged()) {
|
// beware - this state is being reused for error recovery
|
||||||
|
// and if the selector decided to re-home, we have to wait for it as well
|
||||||
|
// therefore: 'if (!mi::idler.Engaged())' : alone is not enough
|
||||||
|
if (!mi::idler.Engaged() && ms::selector.Slot() == mg::globals.ActiveSlot()) {
|
||||||
FinishedCorrectly();
|
FinishedCorrectly();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -97,6 +100,9 @@ bool LoadFilament::StepInner() {
|
||||||
break;
|
break;
|
||||||
case mui::Event::Right: // problem resolved - the user pushed the fillament by hand?
|
case mui::Event::Right: // problem resolved - the user pushed the fillament by hand?
|
||||||
// we should check the state of all the sensors and either report another error or confirm the correct state
|
// we should check the state of all the sensors and either report another error or confirm the correct state
|
||||||
|
|
||||||
|
// First invalidate homing flags as the user may have moved the Idler or Selector accidentally
|
||||||
|
InvalidateHoming();
|
||||||
if (!mf::finda.Pressed()) {
|
if (!mf::finda.Pressed()) {
|
||||||
// FINDA is still NOT pressed - that smells bad
|
// FINDA is still NOT pressed - that smells bad
|
||||||
error = ErrorCode::FINDA_DIDNT_SWITCH_ON;
|
error = ErrorCode::FINDA_DIDNT_SWITCH_ON;
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,9 @@ bool ToolChange::StepInner() {
|
||||||
break;
|
break;
|
||||||
case mui::Event::Right: // problem resolved - the user pushed the fillament by hand?
|
case mui::Event::Right: // problem resolved - the user pushed the fillament by hand?
|
||||||
// we should check the state of all the sensors and either report another error or confirm the correct state
|
// we should check the state of all the sensors and either report another error or confirm the correct state
|
||||||
|
|
||||||
|
// First invalidate homing flags as the user may have moved the Idler or Selector accidentally
|
||||||
|
InvalidateHoming();
|
||||||
if (!mf::finda.Pressed()) {
|
if (!mf::finda.Pressed()) {
|
||||||
// FINDA is still NOT pressed - that smells bad
|
// FINDA is still NOT pressed - that smells bad
|
||||||
error = ErrorCode::FINDA_DIDNT_SWITCH_ON;
|
error = ErrorCode::FINDA_DIDNT_SWITCH_ON;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "../modules/leds.h"
|
#include "../modules/leds.h"
|
||||||
#include "../modules/motion.h"
|
#include "../modules/motion.h"
|
||||||
#include "../modules/permanent_storage.h"
|
#include "../modules/permanent_storage.h"
|
||||||
|
#include "../modules/selector.h"
|
||||||
#include "../modules/user_input.h"
|
#include "../modules/user_input.h"
|
||||||
#include "../debug.h"
|
#include "../debug.h"
|
||||||
|
|
||||||
|
|
@ -93,6 +94,9 @@ bool UnloadFilament::StepInner() {
|
||||||
break;
|
break;
|
||||||
case mui::Event::Right: // problem resolved - the user pulled the fillament by hand
|
case mui::Event::Right: // problem resolved - the user pulled the fillament by hand
|
||||||
// we should check the state of all the sensors and either report another error or confirm the correct state
|
// we should check the state of all the sensors and either report another error or confirm the correct state
|
||||||
|
|
||||||
|
// First invalidate homing flags as the user may have moved the Idler or Selector accidentally
|
||||||
|
InvalidateHoming();
|
||||||
if (mfs::fsensor.Pressed()) {
|
if (mfs::fsensor.Pressed()) {
|
||||||
// printer's filament sensor is still pressed - that smells bad
|
// printer's filament sensor is still pressed - that smells bad
|
||||||
error = ErrorCode::FSENSOR_DIDNT_SWITCH_OFF;
|
error = ErrorCode::FSENSOR_DIDNT_SWITCH_OFF;
|
||||||
|
|
|
||||||
11
src/main.cpp
11
src/main.cpp
|
|
@ -102,10 +102,9 @@ void setup() {
|
||||||
|
|
||||||
mu::cdc.Init();
|
mu::cdc.Init();
|
||||||
|
|
||||||
ms::selector.Init(); // selector decides if homing is possible
|
// waits at least finda debounce period
|
||||||
mi::idler.Home(); // home Idler every time
|
// which is abused to let the LEDs shine for ~100ms
|
||||||
|
mf::finda.BlockingInit();
|
||||||
_delay_ms(100);
|
|
||||||
|
|
||||||
/// Turn off all leds
|
/// Turn off all leds
|
||||||
for (uint8_t i = 0; i < config::toolCount; i++) {
|
for (uint8_t i = 0; i < config::toolCount; i++) {
|
||||||
|
|
@ -113,6 +112,10 @@ void setup() {
|
||||||
ml::leds.SetMode(i, ml::red, ml::off);
|
ml::leds.SetMode(i, ml::red, ml::off);
|
||||||
}
|
}
|
||||||
ml::leds.Step();
|
ml::leds.Step();
|
||||||
|
|
||||||
|
// Idler will home on its own by default
|
||||||
|
// Selector decides whether homing is possible
|
||||||
|
ms::selector.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const uint8_t maxMsgLen = 10;
|
static constexpr const uint8_t maxMsgLen = 10;
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,16 @@ namespace finda {
|
||||||
FINDA finda;
|
FINDA finda;
|
||||||
|
|
||||||
void FINDA::Step() {
|
void FINDA::Step() {
|
||||||
|
|
||||||
debounce::Debouncer::Step(mt::timebase.Millis(), hal::gpio::ReadPin(FINDA_PIN) == hal::gpio::Level::high);
|
debounce::Debouncer::Step(mt::timebase.Millis(), hal::gpio::ReadPin(FINDA_PIN) == hal::gpio::Level::high);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FINDA::BlockingInit() {
|
||||||
|
auto tgtMs = mt::timebase.Millis() + config::findaDebounceMs + 1;
|
||||||
|
Step(); // let FINDA settle down - we're gonna need its state for selector homing
|
||||||
|
while (tgtMs < mt::timebase.Millis()) {
|
||||||
|
mf::finda.Step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace finda
|
} // namespace finda
|
||||||
} // namespace modules
|
} // namespace modules
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ public:
|
||||||
/// Performs one step of the state machine - reads the digital pin, processes debouncing, updates states of FINDA
|
/// Performs one step of the state machine - reads the digital pin, processes debouncing, updates states of FINDA
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
|
/// Initialize the FINDA - waits at least config::findaDebounceMs
|
||||||
|
/// to set correct FINDA state at startup
|
||||||
|
void BlockingInit();
|
||||||
|
|
||||||
using debounce::Debouncer::Pressed;
|
using debounce::Debouncer::Pressed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,11 @@ void Idler::PlanHomingMove() {
|
||||||
|
|
||||||
void Idler::FinishHomingAndPlanMoveToParkPos() {
|
void Idler::FinishHomingAndPlanMoveToParkPos() {
|
||||||
mm::motion.SetPosition(mm::Idler, 0);
|
mm::motion.SetPosition(mm::Idler, 0);
|
||||||
plannedSlot = IdleSlotIndex();
|
|
||||||
plannedEngage = false;
|
// finish whatever has been planned before homing
|
||||||
|
if (!plannedEngage) {
|
||||||
|
plannedSlot = IdleSlotIndex();
|
||||||
|
}
|
||||||
InitMovement(mm::Idler);
|
InitMovement(mm::Idler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,10 +45,19 @@ Idler::OperationResult Idler::Disengage() {
|
||||||
plannedSlot = IdleSlotIndex();
|
plannedSlot = IdleSlotIndex();
|
||||||
plannedEngage = false;
|
plannedEngage = false;
|
||||||
|
|
||||||
|
// coordinates invalid, first home, then disengage
|
||||||
|
if (!homingValid) {
|
||||||
|
PerformHome(mm::Idler);
|
||||||
|
return OperationResult::Accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// already disengaged
|
||||||
if (!Engaged()) {
|
if (!Engaged()) {
|
||||||
dbg_logic_P(PSTR("Idler Disengaged"));
|
dbg_logic_P(PSTR("Idler Disengaged"));
|
||||||
return OperationResult::Accepted;
|
return OperationResult::Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disengaging
|
||||||
return InitMovement(mm::Idler);
|
return InitMovement(mm::Idler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,22 +70,27 @@ Idler::OperationResult Idler::Engage(uint8_t slot) {
|
||||||
plannedSlot = slot;
|
plannedSlot = slot;
|
||||||
plannedEngage = true;
|
plannedEngage = true;
|
||||||
|
|
||||||
|
// if we are homing right now, just record the desired planned slot and return Accepted
|
||||||
|
if (state == Homing) {
|
||||||
|
return OperationResult::Accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// coordinates invalid, first home, then engage
|
||||||
|
if (!homingValid) {
|
||||||
|
PlanHome(mm::Idler);
|
||||||
|
return OperationResult::Accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// already engaged
|
||||||
if (Engaged()) {
|
if (Engaged()) {
|
||||||
dbg_logic_P(PSTR("Idler Engaged"));
|
dbg_logic_P(PSTR("Idler Engaged"));
|
||||||
return OperationResult::Accepted;
|
return OperationResult::Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// engaging
|
||||||
return InitMovement(mm::Idler);
|
return InitMovement(mm::Idler);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Idler::Home() {
|
|
||||||
if (state == Moving)
|
|
||||||
return false;
|
|
||||||
plannedEngage = false;
|
|
||||||
PlanHome(mm::Idler);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Idler::Step() {
|
bool Idler::Step() {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Moving:
|
case Moving:
|
||||||
|
|
@ -85,6 +102,10 @@ bool Idler::Step() {
|
||||||
PerformHome(mm::Idler);
|
PerformHome(mm::Idler);
|
||||||
return false;
|
return false;
|
||||||
case Ready:
|
case Ready:
|
||||||
|
if (!homingValid) {
|
||||||
|
PlanHome(mm::Idler);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
case Failed:
|
case Failed:
|
||||||
dbg_logic_P(PSTR("Idler Failed"));
|
dbg_logic_P(PSTR("Idler Failed"));
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,6 @@ public:
|
||||||
/// @returns #OperationResult
|
/// @returns #OperationResult
|
||||||
OperationResult Disengage();
|
OperationResult Disengage();
|
||||||
|
|
||||||
/// Plan homing of the idler axis
|
|
||||||
/// @returns false in case an operation is already underway
|
|
||||||
bool Home();
|
|
||||||
|
|
||||||
/// Performs one step of the state machine according to currently planned operation
|
/// Performs one step of the state machine according to currently planned operation
|
||||||
/// @returns true if the idler is ready to accept new commands (i.e. it has finished the last operation)
|
/// @returns true if the idler is ready to accept new commands (i.e. it has finished the last operation)
|
||||||
bool Step();
|
bool Step();
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,12 @@ void MovableBase::PerformHome(config::Axis axis) {
|
||||||
// we have reached the end of the axis - homed ok
|
// we have reached the end of the axis - homed ok
|
||||||
mm::motion.AbortPlannedMoves(axis, true);
|
mm::motion.AbortPlannedMoves(axis, true);
|
||||||
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
|
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
|
||||||
|
homingValid = true;
|
||||||
FinishHomingAndPlanMoveToParkPos();
|
FinishHomingAndPlanMoveToParkPos();
|
||||||
// state = Ready; // not yet - we have to move to our parking position after homing the axis
|
// state = Ready; // not yet - we have to move to our parking position after homing the axis
|
||||||
} else if (mm::motion.QueueEmpty(axis)) {
|
} else if (mm::motion.QueueEmpty(axis)) {
|
||||||
// we ran out of planned moves but no StallGuard event has occurred - homing failed
|
// we ran out of planned moves but no StallGuard event has occurred - homing failed
|
||||||
|
homingValid = false;
|
||||||
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
|
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
|
||||||
state = Failed;
|
state = Failed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ public:
|
||||||
inline constexpr MovableBase()
|
inline constexpr MovableBase()
|
||||||
: state(Ready)
|
: state(Ready)
|
||||||
, plannedSlot(-1)
|
, plannedSlot(-1)
|
||||||
, currentSlot(-1) {}
|
, currentSlot(-1)
|
||||||
|
, homingValid(false) {}
|
||||||
|
|
||||||
/// virtual ~MovableBase(); intentionally disabled, see description in logic::CommandBase
|
/// virtual ~MovableBase(); intentionally disabled, see description in logic::CommandBase
|
||||||
|
|
||||||
|
|
@ -42,8 +43,17 @@ public:
|
||||||
|
|
||||||
inline hal::tmc2130::ErrorFlags TMCErrorFlags() const { return tmcErrorFlags; }
|
inline hal::tmc2130::ErrorFlags TMCErrorFlags() const { return tmcErrorFlags; }
|
||||||
|
|
||||||
/// Prepare a homing move of the axis
|
/// Invalidates the homing flag - that is now used to inform the movable component (Idler or Selector)
|
||||||
void PlanHome(config::Axis axis);
|
/// that their current coordinates may have been compromised and a new homing move is to be performed.
|
||||||
|
/// Each movable component performs the homing move immediately after it is possible to do so:
|
||||||
|
/// - Idler immediately (and then moves to desired slot again)
|
||||||
|
/// - Selector once there is no filament stuck in it (and then moves to desired slot again)
|
||||||
|
/// Homing procedure therefore becomes completely transparent to upper layers
|
||||||
|
/// and it will not be necessary to call it explicitly.
|
||||||
|
/// Please note this method does not clear any planned move on the component
|
||||||
|
/// - on the contrary - the planned move will be peformed immediately after homing
|
||||||
|
/// (which makes homing completely transparent)
|
||||||
|
inline void InvalidateHoming() { homingValid = false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// internal state of the automaton
|
/// internal state of the automaton
|
||||||
|
|
@ -55,6 +65,9 @@ protected:
|
||||||
/// current slot
|
/// current slot
|
||||||
uint8_t currentSlot;
|
uint8_t currentSlot;
|
||||||
|
|
||||||
|
/// true if the axis is considered as homed
|
||||||
|
bool homingValid;
|
||||||
|
|
||||||
/// cached TMC2130 error flags - being read only if the axis is enabled and doing something (moving)
|
/// cached TMC2130 error flags - being read only if the axis is enabled and doing something (moving)
|
||||||
hal::tmc2130::ErrorFlags tmcErrorFlags;
|
hal::tmc2130::ErrorFlags tmcErrorFlags;
|
||||||
|
|
||||||
|
|
@ -65,6 +78,9 @@ protected:
|
||||||
|
|
||||||
OperationResult InitMovement(config::Axis axis);
|
OperationResult InitMovement(config::Axis axis);
|
||||||
|
|
||||||
|
/// Prepare a homing move of the axis
|
||||||
|
void PlanHome(config::Axis axis);
|
||||||
|
|
||||||
void PerformMove(config::Axis axis);
|
void PerformMove(config::Axis axis);
|
||||||
|
|
||||||
void PerformHome(config::Axis axis);
|
void PerformHome(config::Axis axis);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
/// @file selector.cpp
|
/// @file selector.cpp
|
||||||
#include "selector.h"
|
#include "selector.h"
|
||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
|
#include "finda.h"
|
||||||
#include "leds.h"
|
#include "leds.h"
|
||||||
#include "motion.h"
|
#include "motion.h"
|
||||||
#include "permanent_storage.h"
|
#include "permanent_storage.h"
|
||||||
|
|
@ -24,7 +25,12 @@ void Selector::PlanHomingMove() {
|
||||||
|
|
||||||
void Selector::FinishHomingAndPlanMoveToParkPos() {
|
void Selector::FinishHomingAndPlanMoveToParkPos() {
|
||||||
mm::motion.SetPosition(mm::Selector, mm::unitToSteps<mm::S_pos_t>(config::selectorLimits.lenght));
|
mm::motion.SetPosition(mm::Selector, mm::unitToSteps<mm::S_pos_t>(config::selectorLimits.lenght));
|
||||||
plannedSlot = IdleSlotIndex();
|
currentSlot = -1;
|
||||||
|
|
||||||
|
// finish whatever has been planned before homing
|
||||||
|
if (plannedSlot > config::toolCount) {
|
||||||
|
plannedSlot = IdleSlotIndex();
|
||||||
|
}
|
||||||
InitMovement(mm::Selector);
|
InitMovement(mm::Selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,18 +45,25 @@ Selector::OperationResult Selector::MoveToSlot(uint8_t slot) {
|
||||||
}
|
}
|
||||||
plannedSlot = slot;
|
plannedSlot = slot;
|
||||||
|
|
||||||
|
// if we are homing right now, just record the desired planned slot and return Accepted
|
||||||
|
if (state == Homing) {
|
||||||
|
return OperationResult::Accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// coordinates invalid, first home, then engage
|
||||||
|
if (!homingValid && mg::globals.FilamentLoaded() < mg::FilamentLoadState::InSelector) {
|
||||||
|
PlanHome(mm::Selector);
|
||||||
|
return OperationResult::Accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
// already at the right slot
|
||||||
if (currentSlot == slot) {
|
if (currentSlot == slot) {
|
||||||
dbg_logic_P(PSTR("Moving Selector"));
|
dbg_logic_P(PSTR("Moving Selector"));
|
||||||
return OperationResult::Accepted;
|
return OperationResult::Accepted;
|
||||||
}
|
}
|
||||||
return InitMovement(mm::Selector);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Selector::Home() {
|
// do the move
|
||||||
if (state == Moving)
|
return InitMovement(mm::Selector);
|
||||||
return false;
|
|
||||||
PlanHome(mm::Selector);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Selector::Step() {
|
bool Selector::Step() {
|
||||||
|
|
@ -64,7 +77,10 @@ bool Selector::Step() {
|
||||||
PerformHome(mm::Selector);
|
PerformHome(mm::Selector);
|
||||||
return false;
|
return false;
|
||||||
case Ready:
|
case Ready:
|
||||||
//dbg_logic_P(PSTR("Selector Ready"));
|
if (!homingValid && mg::globals.FilamentLoaded() < mg::InSelector) {
|
||||||
|
PlanHome(mm::Selector);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
case Failed:
|
case Failed:
|
||||||
dbg_logic_P(PSTR("Selector Failed"));
|
dbg_logic_P(PSTR("Selector Failed"));
|
||||||
|
|
@ -74,12 +90,13 @@ bool Selector::Step() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selector::Init() {
|
void Selector::Init() {
|
||||||
if (mg::globals.FilamentLoaded() < mg::FilamentLoadState::InSelector) {
|
if (mg::globals.FilamentLoaded() < mg::FilamentLoadState::InSelector && (!mf::finda.Pressed())) {
|
||||||
// home the Selector only in case we don't have filament loaded (or at least we think we don't)
|
// home the Selector only in case we don't have filament loaded (or at least we think we don't)
|
||||||
Home();
|
PlanHome(mm::Selector);
|
||||||
} else {
|
} else {
|
||||||
// otherwise set selector's position according to know slot positions (and pretend it is correct)
|
// otherwise set selector's position according to know slot positions (and pretend it is correct)
|
||||||
mm::motion.SetPosition(mm::Selector, SlotPosition(mg::globals.ActiveSlot()).v);
|
mm::motion.SetPosition(mm::Selector, SlotPosition(mg::globals.ActiveSlot()).v);
|
||||||
|
InvalidateHoming(); // and plan homing sequence ASAP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@ public:
|
||||||
/// @returns false in case an operation is already underway
|
/// @returns false in case an operation is already underway
|
||||||
OperationResult MoveToSlot(uint8_t slot);
|
OperationResult MoveToSlot(uint8_t slot);
|
||||||
|
|
||||||
/// Plan homing of the selector's axis
|
|
||||||
/// @returns false in case an operation is already underway
|
|
||||||
bool Home();
|
|
||||||
|
|
||||||
/// Performs one step of the state machine according to currently planned operation.
|
/// Performs one step of the state machine according to currently planned operation.
|
||||||
/// @returns true if the selector is ready to accept new commands (i.e. it has finished the last operation)
|
/// @returns true if the selector is ready to accept new commands (i.e. it has finished the last operation)
|
||||||
bool Step();
|
bool Step();
|
||||||
|
|
|
||||||
|
|
@ -137,8 +137,11 @@ void FailedLoadToFindaResolveManual(uint8_t slot, logic::LoadFilament &lf) {
|
||||||
hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::high);
|
hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::high);
|
||||||
PressButtonAndDebounce(lf, mb::Right);
|
PressButtonAndDebounce(lf, mb::Right);
|
||||||
|
|
||||||
|
// the Idler also engages in this call as this is planned as the next step
|
||||||
|
SimulateIdlerHoming();
|
||||||
|
|
||||||
// pulling filament back
|
// pulling filament back
|
||||||
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, true, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::RetractingFromFinda));
|
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, slot, slot, true, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::RetractingFromFinda));
|
||||||
|
|
||||||
// Stage 3 - retracting from finda
|
// Stage 3 - retracting from finda
|
||||||
// we'll assume the finda is working correctly here
|
// we'll assume the finda is working correctly here
|
||||||
|
|
@ -150,9 +153,24 @@ void FailedLoadToFindaResolveManual(uint8_t slot, logic::LoadFilament &lf) {
|
||||||
}
|
}
|
||||||
return lf.TopLevelState() == ProgressCode::RetractingFromFinda; },
|
return lf.TopLevelState() == ProgressCode::RetractingFromFinda; },
|
||||||
5000));
|
5000));
|
||||||
REQUIRE(VerifyState(lf, mg::FilamentLoadState::AtPulley, slot, slot, false, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::DisengagingIdler));
|
|
||||||
|
|
||||||
// disengaging idler
|
// This is a tricky part as the Selector will start homing asynchronnously right after
|
||||||
|
// the filament state switches to AtPulley.
|
||||||
|
// The trouble is, that the filament state is updated after the Pulley finishes
|
||||||
|
// its moves (which is correct), but we don't have enough cycles to home the selector afterwards
|
||||||
|
// - basically it will just start homing
|
||||||
|
// Moreover, the Idler is to disengage meanwhile, which makes the simulation even harder.
|
||||||
|
// Therefore we just tick the stallguard of the Selector and hope for the best
|
||||||
|
mm::TriggerStallGuard(mm::Selector);
|
||||||
|
ms::selector.Step();
|
||||||
|
mm::motion.StallGuardReset(mm::Selector); // drop stallguard on Selector to avoid future confusion
|
||||||
|
|
||||||
|
// just one step is necessary to "finish" homing
|
||||||
|
// but the selector then (correctly) plans its move to the original position
|
||||||
|
// therefore we expect the selector to have its idle position at this stage
|
||||||
|
// REQUIRE(VerifyState(lf, mg::FilamentLoadState::AtPulley, slot, ms::selector.IdleSlotIndex(), false, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::DisengagingIdler));
|
||||||
|
|
||||||
|
// disengaging idler (and the selector will move to the desired position meanwhile
|
||||||
REQUIRE(WhileTopState(lf, ProgressCode::DisengagingIdler, idlerEngageDisengageMaxSteps));
|
REQUIRE(WhileTopState(lf, ProgressCode::DisengagingIdler, idlerEngageDisengageMaxSteps));
|
||||||
REQUIRE(VerifyState(lf, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), slot, false, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK));
|
REQUIRE(VerifyState(lf, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), slot, false, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK));
|
||||||
}
|
}
|
||||||
|
|
@ -161,6 +179,8 @@ void FailedLoadToFindaResolveManualNoFINDA(uint8_t slot, logic::LoadFilament &lf
|
||||||
// Perform press on button 2 + debounce + keep FINDA OFF (i.e. the user didn't solve anything)
|
// Perform press on button 2 + debounce + keep FINDA OFF (i.e. the user didn't solve anything)
|
||||||
PressButtonAndDebounce(lf, mb::Right);
|
PressButtonAndDebounce(lf, mb::Right);
|
||||||
|
|
||||||
|
SimulateIdlerHoming();
|
||||||
|
|
||||||
// pulling filament back
|
// pulling filament back
|
||||||
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, false, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERRWaitingForUser));
|
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, false, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERRWaitingForUser));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,12 @@ void ForceReinitAllAutomata() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HomeIdlerAndSelector() {
|
void HomeIdlerAndSelector() {
|
||||||
ms::selector.Home();
|
ms::selector.InvalidateHoming();
|
||||||
mi::idler.Home();
|
mi::idler.InvalidateHoming();
|
||||||
|
SimulateIdlerAndSelectorHoming();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulateIdlerAndSelectorHoming() {
|
||||||
// do 5 steps until we trigger the simulated stallguard
|
// do 5 steps until we trigger the simulated stallguard
|
||||||
for (uint8_t i = 0; i < 5; ++i) {
|
for (uint8_t i = 0; i < 5; ++i) {
|
||||||
main_loop();
|
main_loop();
|
||||||
|
|
@ -86,6 +89,39 @@ void HomeIdlerAndSelector() {
|
||||||
// now the Selector and Idler shall perform a move into their parking positions
|
// now the Selector and Idler shall perform a move into their parking positions
|
||||||
while (ms::selector.State() != mm::MovableBase::Ready || mi::idler.State() != mm::MovableBase::Ready)
|
while (ms::selector.State() != mm::MovableBase::Ready || mi::idler.State() != mm::MovableBase::Ready)
|
||||||
main_loop();
|
main_loop();
|
||||||
|
|
||||||
|
mm::motion.StallGuardReset(mm::Selector);
|
||||||
|
mm::motion.StallGuardReset(mm::Idler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulateIdlerHoming() {
|
||||||
|
// do 5 steps until we trigger the simulated stallguard
|
||||||
|
for (uint8_t i = 0; i < 5; ++i) {
|
||||||
|
main_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
mm::TriggerStallGuard(mm::Idler);
|
||||||
|
|
||||||
|
// now the Idler shall perform a move into their parking positions
|
||||||
|
while (mi::idler.State() != mm::MovableBase::Ready)
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
mm::motion.StallGuardReset(mm::Idler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimulateSelectorHoming() {
|
||||||
|
// do 5 steps until we trigger the simulated stallguard
|
||||||
|
for (uint8_t i = 0; i < 5; ++i) {
|
||||||
|
main_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
mm::TriggerStallGuard(mm::Selector);
|
||||||
|
|
||||||
|
// now the Selector shall perform a move into their parking positions
|
||||||
|
while (ms::selector.State() != mm::MovableBase::Ready)
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
mm::motion.StallGuardReset(mm::Selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureActiveSlotIndex(uint8_t slot, mg::FilamentLoadState loadState) {
|
void EnsureActiveSlotIndex(uint8_t slot, mg::FilamentLoadState loadState) {
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,11 @@ bool WhileTopState(SM &sm, ProgressCode state, uint32_t maxLoops = 5000) {
|
||||||
sm, [&](uint32_t) { return sm.TopLevelState() == state; }, maxLoops);
|
sm, [&](uint32_t) { return sm.TopLevelState() == state; }, maxLoops);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void EnsureActiveSlotIndex(uint8_t slot, modules::globals::FilamentLoadState loadState);
|
void EnsureActiveSlotIndex(uint8_t slot, modules::globals::FilamentLoadState loadState);
|
||||||
|
void SetFINDAStateAndDebounce(bool press);
|
||||||
extern void SetFINDAStateAndDebounce(bool press);
|
void SimulateIdlerHoming();
|
||||||
|
void SimulateSelectorHoming();
|
||||||
|
void SimulateIdlerAndSelectorHoming();
|
||||||
|
|
||||||
// these are recommended max steps for simulated movement of the idler and selector
|
// these are recommended max steps for simulated movement of the idler and selector
|
||||||
// - roughly the amount of motion steps from one end to the other + some margin
|
// - roughly the amount of motion steps from one end to the other + some margin
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue