Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.1.x] Purge blocks on endstop/probe hit #8690

Merged

Conversation

thinkyhead
Copy link
Member

@thinkyhead thinkyhead commented Dec 6, 2017

As an alternative to #8687 / #8688

Rather than rely on code that plans to trigger endstops clearing a flag (planner.split_first_move) to prevent the first move being split, simply make sure to purge the rest of the blocks in the buffer when an endstop or probe move leads to a trigger.

The stepper code should be doing this for safety anyway. Although the high-level code is careful to only do single un-segmented moves when endstops or the probe are enabled, and the G-code parser is blocked during operations like G28 and G29, there are some cases where there could be more than one move in the planner.

When using commands like G38.2 on a Delta or SCARA, a sideways probe will be a segmented move. Likewise, when using the ABORT_ON_ENDSTOP_HIT feature during an SD print, it is entirely possible for extra moves to be in the planner when the abort occurs.

The cleaning_buffer_counter has only been used by quick_stop so far, so this PR modifies it slightly so it can differentiate between an endstop hit and a quick_stop.


The opening code of Stepper::isr differs somewhat between the 1.1.x and 2.0.x branches.

1beaef0#diff-a7c736c674fcde8ddf7bf0e86181e4c0R355

I'd like to resolve which differences are important and get these back in sync asap.

@Roxy-3D
Copy link
Member

Roxy-3D commented Dec 6, 2017

Note that UBL has a tentacle in quick_stop to prevent it from purging remaining blocks when Map Control is turned on, so moves may continue to occur after quick_stop for purposes that are specific to Map Control.

That is only in the case of using the LCD Display to position the nozzle at a new mesh point. The whole intent is if the user is spinning the dial, we don't want the nozzle to trace through every mesh point. Instead, we cancel the current move and start heading towards the (currently) selected mesh point on the LCD Display. This allows a much more responsive edit session.

There should be no harm throwing out any queued motion commands so long as the next destination mesh point has been identified and a move to get there is submitted.

void Stepper::quick_stop() {
  #if ENABLED(AUTO_BED_LEVELING_UBL) && ENABLED(ULTIPANEL)
    if (!ubl.lcd_map_control)
      cleaning_buffer_counter = 5000;
  #else
    cleaning_buffer_counter = 5000;
  #endif

I suspect we can cut out this tentacle. (Or really... The special case for UBL's LCD Mesh Editing) I think we just need to flush the entire queue... And then make sure we queue up a single move that gets the nozzle to the currently selected mesh point.

@thinkyhead
Copy link
Member Author

thinkyhead commented Dec 6, 2017

For Map Control it could be safe to have cleaning_buffer_counter set to some smaller value, like 1 or 2. We definitely want to kill any blocks that might still be in the planner, because they won't chain properly to a block that's been stopped mid-move. The value 5000 leads to the Stepper ISR cleaning blocks for a full 1/2 second. It might be safe for Map Control purposes to set it to a smaller value, or it could still set cleaning_buffer_counter to 5000, but wait in a loop until movesplanned() is zero, and then set cleaning_buffer_counter to zero before adding the new move.

@thinkyhead
Copy link
Member Author

thinkyhead commented Dec 6, 2017

If cleaning_buffer_counter is made public then this can be done like so…

void _lcd_do_nothing() {}
  if (planner.movesplanned() > 1) { // if the nozzle is moving, cancel the move.  There is a new location
    stepper.quick_stop();
+   currentScreen = _lcd_do_nothing;
+   while (planner.movesplanned()) idle();
+   stepper.cleaning_buffer_counter = 0;
+   currentScreen = _lcd_ubl_output_map_lcd;
    set_current_from_steppers_for_axis(ALL_AXES);
    sync_plan_position();
    ubl_map_move_to_xy(); // Move to new location
    refresh_cmd_timeout();
  }

@thinkyhead thinkyhead force-pushed the bf1_clean_blocks_on_trigger branch 2 times, most recently from e608a5d to add2f64 Compare December 6, 2017 22:24
Copy link
Member

@AnHardt AnHardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ingenious.
Up to now i can't find unwanted side effects.

@thinkyhead
Copy link
Member Author

thinkyhead commented Dec 6, 2017

I see that 2.0.x and 1.1.x still don't match in several areas, most notably the ubl_height_adjust family of functions in ultralcd.cpp seem to be more modern and correct in the 1.1.x branch, and have not been ported to the 2.0.x branch.

@Roxy-3D I have one specific question regarding two lines. Which is more current and correct?

      const int ind = ubl_height_amount < 0 ? 6 : 7;
      strcpy_P(UBL_LCD_GCODE, PSTR("G29 P6-"));

…or…

      const int ind = ubl_height_amount > 0 ? 9 : 10;
      strcpy_P(UBL_LCD_GCODE, PSTR("G29 P6 C -"));

@Roxy-3D
Copy link
Member

Roxy-3D commented Dec 6, 2017

I believe this is more correct:

      const int ind = ubl_height_amount > 0 ? 9 : 10;
      strcpy_P(UBL_LCD_GCODE, PSTR("G29 P6 C -"));

The G29 P6 mesh adjust needs a 'C' constant to tell the command how much to shift the mesh up or down.

@thinkyhead thinkyhead force-pushed the bf1_clean_blocks_on_trigger branch 4 times, most recently from 3725da4 to c749c33 Compare December 7, 2017 00:55
@thinkyhead
Copy link
Member Author

I see! So in that case I'll assume the UBL-oriented stuff in 1.1.x (ultralcd.cpp) is most up-to-date and add a commit to the 2.0.x PR to match them up.

@thinkyhead thinkyhead force-pushed the bf1_clean_blocks_on_trigger branch 9 times, most recently from 7bf6aa4 to 320afe0 Compare December 7, 2017 03:13
@thinkyhead thinkyhead merged commit 6c328ec into MarlinFirmware:bugfix-1.1.x Dec 7, 2017
@thinkyhead thinkyhead deleted the bf1_clean_blocks_on_trigger branch December 7, 2017 04:26
@AnHardt
Copy link
Member

AnHardt commented Dec 7, 2017

I hope to know what the reason for #8700 , #8704 is.
I expected to have 0-1 moves in the buffer after the endstop hit. What we do now is a bit of overkill.
We tell the stepper interrupt to delete (BLOCK_BUFFER_SIZE - 1) lines. This will take a while because only one line is deleted at the time. While the stepper isr does its job the main program continues. After a planned endstop hit it normally waits until the buffer is empty (stepper.synchronize()) and begins to produce the next moves to back up, or whatever. These moves are planned and immediately deleted by the still in delete mode running stepper interrupt.
This behaviour was planned for the emergency stop to empty not only the planner buffer, but also all others. For that the high number.
Sorry, could not see that yesterday.
Same in #8691

@AnHardt
Copy link
Member

AnHardt commented Dec 7, 2017

A possible fix could be to set cleaning_buffer_counter to -planner.movesplanned() or to prevent from spliting the moves by my version of the patch.

@AnHardt
Copy link
Member

AnHardt commented Dec 7, 2017

Or

/**
 * Block until all buffered steps are executed or deleted
 */
void Stepper::synchronize() { while (planner.blocks_queued() || cleaning_buffer_counter) idle(); }

@bjarchi
Copy link
Contributor

bjarchi commented Dec 8, 2017

@AnHardt Since I reported #8704, is there anything I can do to help out here with code review or testing or etc? I'm not super-experienced with C family languages but I can get by - especially when I know what I'm supposed to be looking at.

@thinkyhead
Copy link
Member Author

thinkyhead commented Dec 8, 2017

Stepper::synchronize

The stepper synchronize idea seems good, but then there are cases where synchronize isn't guaranteed to be applied immediately after the endstop/probe move.

Here's one idea. We have room for more block flag bits. We could add an EXPENDABLE bit that indicates (for example) that the block comes from a split move. When cleaning_buffer_counter is set to a negative value then it will only throw away blocks marked as expendable and stop as soon as it hits a non-expendable block.

@@ -311,8 +311,7 @@
void unified_bed_leveling::G29() {

if (!settings.calc_num_meshes()) {
SERIAL_PROTOCOLLNPGM("?You need to enable your EEPROM and initialize it");
SERIAL_PROTOCOLLNPGM("with M502, M500, M501 in that order.\n");
SERIAL_PROTOCOLLNPGM("?Enable EEPROM and init with M502, M500.\n");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message confused me when I got it a moment ago. I was doing M502 and M500 but was still getting the message. When I added M501, it started working. How come it was removed from the helpful error message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no reason for M501 to be needed. If M501 is required something is broken. Someday someone will have to look into that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the rationale. UBL doesn't know where it can start storing its meshes until after M501. This is something that we can certainly figure out at compile time, but for now you do need to use M501 as a workaround for this oversight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants