gnea / grbl

An open source, embedded, high performance g-code-parser and CNC milling controller written in optimized C that will run on a straight Arduino
https://github.com/gnea/grbl/wiki
Other
4.1k stars 1.61k forks source link

Homing with no switches? (physical end stops only) #746

Open SketchThis opened 5 years ago

SketchThis commented 5 years ago

Like the title says, is it possible to home a GRBL machine with no limit switches?

Let me explain: I have a CNC I'm converting and it homes itself by jogging each axis farther than it can physically go.

To home each axis there's a hard stop at Z zero, X 12 and Y 12.

The machine's max Z travel is 3 inches, so on homing it jogs up 4 inches and hits the end stop. It backs off .125" and is zero'd there.

Same for X and Y, they're jogged to 13 inches (1" more than the max travel) so they hit the end stop. After that 13" move they're jogged negatively 12" and zero'd there.

Is it possible to set something like this up in GRBLs native homing routine? I'm sure I go do it manually with native G Code commands, but I'd like to have the homing buttons actually work...

bdurbrow commented 5 years ago

Um... that's really kinda sketchy. I think you'd be better off adding limit switches to the endstops.

However, if you really, really must... look at limits.c; specifically limits_go_home().

What you will need to do is remove all the stuff that checks the limit switches, and performs pull-off moves once the limits are found.

This is completely untested; so may not work -- but, I think it should look something like this:

void limits_go_home(uint8_t cycle_mask)
{
  if (sys.abort) { return; } // Block if system reset has been issued.

  // Initialize plan data struct for homing motion. Spindle and coolant are disabled.
  plan_line_data_t plan_data;
  plan_line_data_t *pl_data = &plan_data;
  memset(pl_data,0,sizeof(plan_line_data_t));
  pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
  #ifdef USE_LINE_NUMBERS
    pl_data->line_number = HOMING_CYCLE_LINE_NUMBER;
  #endif

  float max_travel = 0.0;
  uint8_t n_active_axis = 0;
  for (uint8_t idx=0; idx<N_AXIS; idx++)
  {
    if (bit_istrue(cycle_mask,bit(idx)))
    {
      // Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
      // NOTE: settings.max_travel[] is stored as a negative value.
      max_travel = max(max_travel,(-HOMING_AXIS_SEARCH_SCALAR)*settings.max_travel[idx]);
      n_active_axis++;
    }
  }

  float target[N_AXIS];
  system_convert_array_steps_to_mpos(target,sys_position);

  for (idx=0; idx<N_AXIS; idx++)
  {
    // Set target location for active axes and setup computation for homing rate.
    if (bit_istrue(cycle_mask,bit(idx)))
    {
      sys_position[idx] = 0;

      // Set target direction based on cycle mask and homing cycle approach state.
      // NOTE: This happens to compile smaller than any other implementation tried.
      if (bit_istrue(settings.homing_dir_mask,bit(idx)))
      {
        target[idx] = -max_travel;
      }
      else
      {
        target[idx] = max_travel;
      }      
    }
  }

  // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
  float homing_rate = settings.homing_seek_rate;
  homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
  pl_data->feed_rate = homing_rate; // Set current homing rate.
  plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion.
  sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags.
  st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
  st_wake_up(); // Initiate motion

    do
    {
      st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.

      // Exit routines: No time to run protocol_execute_realtime() in this loop.
      if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP))
      {
        uint8_t rt_exec = sys_rt_exec_state;
        // Homing failure condition: Reset issued during cycle.
        if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
        // Homing failure condition: Safety door was opened.
        if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
        // Homing failure condition: Limit switch not found during approach.
        if (sys_rt_exec_alarm)
        {
          mc_reset(); // Stop motors, if they are running.
          protocol_execute_realtime();
          return;
        }
      }
    } while (!(rt_exec & EXEC_CYCLE_STOP));

  // The active cycle axes should now be homed and machine limits have been located. By
  // default, Grbl defines machine space as all negative, as do most CNCs. Since limit stops
  // can be on either side of an axes, check and set axes machine zero appropriately.

  int32_t set_axis_position;
  // Set machine positions for homed limit switches. Don't update non-homed axes.
  for (idx=0; idx<N_AXIS; idx++)
  {
    // NOTE: settings.max_travel[] is stored as a negative value.
    if (cycle_mask & bit(idx))
    {
      #ifdef HOMING_FORCE_SET_ORIGIN
        set_axis_position = 0;
      #else
        if ( bit_istrue(settings.homing_dir_mask,bit(idx)) )
        {
          set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
        }
        else
        {
          set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
        }
      #endif

      sys_position[idx] = set_axis_position;
    }
  }
  sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
}
SketchThis commented 5 years ago

Thank you so much for that, I wasn't even sure where to start looking. I know this sounds sketchy, but that's exactly how this machine was built new. The end stops are actually adjustable so you can dial them in exactly where you want them. During the homing routine the machine sneaks up on the end stops at a reasonably slow speed. There is still a touch off plate for Z.

I'll hack around in there and see what I can come up with!

SketchThis commented 5 years ago

Alright, I tried compiling that code and it failed. I don't fully (or even partially!) understand the programming language here...

Not that I want anyone to do the work for me here... But would anyone care to help me dig in a little deeper to see if I could solve this?

bdurbrow commented 5 years ago

Sorry - I found a few typos in there. This has been tested to actually compile (however, I have no hardware to test it on - everything I'm doing uses my own fork of Grbl-Mega; and has homing switches). Um... Are you really, really sure you don't want to add some limit switches to it? Really?


void limits_go_home(uint8_t cycle_mask)
{
  if (sys.abort) { return; } // Block if system reset has been issued.

  // Initialize plan data struct for homing motion. Spindle and coolant are disabled.
  plan_line_data_t plan_data;
  plan_line_data_t *pl_data = &plan_data;
  memset(pl_data,0,sizeof(plan_line_data_t));
  pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION|PL_COND_FLAG_NO_FEED_OVERRIDE);
  #ifdef USE_LINE_NUMBERS
    pl_data->line_number = HOMING_CYCLE_LINE_NUMBER;
  #endif

  float max_travel = 0.0;
  uint8_t n_active_axis = 0;
  for (uint8_t idx=0; idx<N_AXIS; idx++)
  {
    if (bit_istrue(cycle_mask,bit(idx)))
    {
      // Set target based on max_travel setting. Ensure homing switches engaged with search scalar.
      // NOTE: settings.max_travel[] is stored as a negative value.
      max_travel = max(max_travel,(-HOMING_AXIS_SEARCH_SCALAR)*settings.max_travel[idx]);
      n_active_axis++;
    }
  }

  float target[N_AXIS];
  system_convert_array_steps_to_mpos(target,sys_position);

  for (uint8_t idx=0; idx<N_AXIS; idx++)
  {
    // Set target location for active axes and setup computation for homing rate.
    if (bit_istrue(cycle_mask,bit(idx)))
    {
      sys_position[idx] = 0;

      // Set target direction based on cycle mask and homing cycle approach state.
      // NOTE: This happens to compile smaller than any other implementation tried.
      if (bit_istrue(settings.homing_dir_mask,bit(idx)))
      {
        target[idx] = -max_travel;
      }
      else
      {
        target[idx] = max_travel;
      }      
    }
  }

  // Perform homing cycle. Planner buffer should be empty, as required to initiate the homing cycle.
  float homing_rate = settings.homing_seek_rate;
  homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
  pl_data->feed_rate = homing_rate; // Set current homing rate.
  plan_buffer_line(target, pl_data); // Bypass mc_line(). Directly plan homing motion.
  sys.step_control = STEP_CONTROL_EXECUTE_SYS_MOTION; // Set to execute homing motion and clear existing flags.
  st_prep_buffer(); // Prep and fill segment buffer from newly planned block.
  st_wake_up(); // Initiate motion

    uint8_t rt_exec;
    do
    {
      st_prep_buffer(); // Check and prep segment buffer. NOTE: Should take no longer than 200us.

      // Exit routines: No time to run protocol_execute_realtime() in this loop.
      rt_exec = sys_rt_exec_state;
      if (rt_exec & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP))
      {
        // Homing failure condition: Reset issued during cycle.
        if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
        // Homing failure condition: Safety door was opened.
        if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
        // Homing failure condition: Limit switch not found during approach.
        if (sys_rt_exec_alarm)
        {
          mc_reset(); // Stop motors, if they are running.
          protocol_execute_realtime();
          return;
        }
      }
    } while (!(rt_exec & EXEC_CYCLE_STOP));

  // The active cycle axes should now be homed and machine limits have been located. By
  // default, Grbl defines machine space as all negative, as do most CNCs. Since limit stops
  // can be on either side of an axes, check and set axes machine zero appropriately.

  int32_t set_axis_position;
  // Set machine positions for homed limit switches. Don't update non-homed axes.
  for (uint8_t idx=0; idx<N_AXIS; idx++)
  {
    // NOTE: settings.max_travel[] is stored as a negative value.
    if (cycle_mask & bit(idx))
    {
      #ifdef HOMING_FORCE_SET_ORIGIN
        set_axis_position = 0;
      #else
        if ( bit_istrue(settings.homing_dir_mask,bit(idx)) )
        {
          set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
        }
        else
        {
          set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
        }
      #endif

      sys_position[idx] = set_axis_position;
    }
  }
  sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
}
SketchThis commented 5 years ago

I know it sounds like a bad idea to have no prox switches... But this machine was designed and built to work without them:

https://store.handibot.com/collections/whats-new/products/handibot-smart-power-tool-v2-1-adventure-edition

There are solid adjustable end stops on each axis.

That's what I'm converting. I have removed the stock motion control board and already replaced it with an Arduino Uno. Everything works, I just need to get this homing thing sorted out. I know I could put some prox switches in there, but if possible I'd like to skip them so that the machine works like it did from the factory. This way one could swap over to a GRBL control without having to modify the machine in any way, just remove the stock motion card (a single Db 37 connector) and plug in an Arduino.

The other wrinkle I'm wondering if I'll run into.. The way this machine homes now is that it jogs Z all the way up and hits the stop, then jogs to X 8, hits the stop, and then to Y0 and hits the stop. However the zero position is on the bottom left corner. My guess is that beyond altering the code for the limit switches some other modification would be needed to the code...

I'm not opposed to putting limit switches on this thing, I just think it's far cooler to do it as a single plug swap over.

I'll give your code a shot this weekend. THANK YOU!

bdurbrow commented 5 years ago

Well, as you’ve probably guessed by now, I am of the opinion that that’s a bad design from the factory. 😏

The homing direction can be setup with the standard parameters - I left that part intact when I stripped down the homing routine; check out the wiki article on setting up homing for more info. Just substitute your end-stops for the switches mentioned in the article.

SketchThis commented 5 years ago

Thanks for your work on this! I'll give it a shot tomorrow when I'm back in the shop.

I know I can flip the homing direction easily... What I'm wondering is how zeroing will work... Two out of the three end stops are at zero but one is at the max travel.... As of yet I don't know if I can set machine coordinates at a non zero number when an axis hits the end stops regardless if I'm using prox switches it not ..

bdurbrow commented 5 years ago

You should be able to do what you want with the standard direction control settings; but if you really need to you can flip the direction that it heads with these lines:

      if (bit_istrue(settings.homing_dir_mask,bit(idx)))
      {
        target[idx] = -max_travel;
      }
      else
      {
        target[idx] = max_travel;
      }      

Just remove the minus sign from one max_travel and add it to the other.

The zeroing is done with these lines:

        if ( bit_istrue(settings.homing_dir_mask,bit(idx)) )
        {
          set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
        }
        else
        {
          set_axis_position = lround(-settings.homing_pulloff*settings.steps_per_mm[idx]);
        }
SketchThis commented 5 years ago

Ok, I wasn't able to get this to work, it just hung during homing. Since I don't know enough about coding I had no real way to troubleshoot so I threw in the towel and I added limit switches to it. :)

bdurbrow commented 5 years ago

Ok, I wasn't able to get this to work, it just hung during homing.

Odd... obviously I haven't been able to test it, I don't have a machine that homes by crashing into the stops... but hanging isn't one of my expected outcomes. No matter what it should exit the homing loop when the move is complete.

Specifically, this: while (!(rt_exec & EXEC_CYCLE_STOP));

In any case - I do think that in the long run, you'll be better off with the limit switches.

😄

drf5n commented 22 hours ago

You could turn homing off and do the homing cycle in GCODE, and then set the origin:

$22=0

G01 X13 Y13 Z-4 F100
G01 X1 Y1 Z-3.857 
G92