Changing limit from NO to NC - MoboTools

Missing something so simple I can't see it

I've got a rail running and a stepper that goes and sets it's home position from a limit switch. Works fine.
I thought about it and decided that the limit switch should be a NC switch instead of a NO one so I changed the wiring and then when I look at the code I thought no problem, but do I connect the switch to 3.3v power (eps32) instead of ground and then change from

while (!Buttons.state(UP_LMT_SW)) // works with NO switch
to
while (Buttons.state(UP_LMT_SW))

Would the switch need to be wiring to the 3.3V instead of the ground ?????

Here's the homing function

void Home() // Home the machine to the upper limit switch 
{
  myStepper.setZero(0);               
  myStepper.setSpeed( 1500 );         // slow speed for homing
  
  // move the carrige down some to make sure it's off the switch
  myStepper.moveTo(1400); 
  while ( myStepper.distanceToGo()!=0 )
  {
    Serial.println("Staging");
    myStepper.moveTo(1400);   
  }

  while (HOMED == false )           // move up to home switch
   {
    Serial.println("Homing");
    //while (!Buttons.state(HOME_SW))  <<<<<<<<<<<< NO switch works fine
    while (Buttons.state(UP_LMT_SW))  <<<<<<<<<<<< NC switch 
      {
        Buttons.processButtons();
        myStepper.moveTo(myStepper.currentPosition()-400);    // keep moving up
      }
    myStepper.stop();
    myStepper.setZero(1200);          // come down off the switch
    myStepper.moveTo(0);
    HOMED=true;
    Serial.println("HOMED");
    
   }
}

EDIT

  • Lets assume this works:


//5V---Internal PullUp---Pin---[N.O. Switch]---GND
#define switchNotOperated    HIGH  // N.O. switch

. . . 

while (digitalRead(UP_LMT_SW) == switchNotOperated )
  {
    //do something 
  }



  • Now we move to a N.C. switch
//5V---Internal PullUp---Pin---[N.C. Switch]---GND
#define switchNotOperated    LOW // N.C. switch

. . .
//while (digitalRead(UP_LMT_SW == switchNotOperated)  error in this line
while (digitalRead(UP_LMT_SW) == switchNotOperated)
  {
    //do something 
  }

I don't think you need to change the wiring, just put the NC switch exactly where the NO switch is.

Then in the one place you read the switch, change the sense of what HIGH and LOW mean.

This is exactly what @LarryD has said, we are both at the disadvantage of not seeing where the switch does get read.

If the digitalRead() is in code you aren't seeing, you'll have to do the sense change higher up, your idea was odd as you referred to two different switches. I would expect it to look like this

    //while (!Buttons.state(UP_LMT_SW))  <<<<<<<<<<<< NO switch works fine
    while (Buttons.state(UP_LMT_SW))  <<<<<<<<<<<< NC switch 

but that can benefit from the same kind of treatment @LarryD applied to digitalRead().

a7

Fixed that for ya'.

a7

2 Likes

The switch must still be connected to Gnd as @alto777 already said ). MoToButtons always needs the switches to be connected to Gnd. Only change the logic ( as you did ).

That's what I thought but when I changed the logic from
while (!Buttons.state(UP_LMT_SW))
to
while (Buttons.state(UP_LMT_SW))

it doesn't work. The stepper drives the carriage down "off the switch", then stops, doesn't reverse onto the switch and then goes down the last steps. So where it's supposed to be moving up until the switch is hit, it sees the switch as already hit and goes to the last step where it goes down off the switch again.

When I trip the switch manually and start the sequence it does the same thing.

When I put a meter on the ground to switch/esp32 connection it is at zero and then 3.3v when the switch is activated so I'm not sure where it's getting the 3.3v from other than the esp32 itself (internal pull up?)

if I put the switch back to NO and use while (!Buttons.state(UP_LMT_SW)) it works proper

The switch read initially happens prior to the call for the function and then again inside the while loop

Buttons.processButtons();

is part of the mobatools library I'm using that checks the current state of all of the switches being used

That's why I thought that just changing the while (!Buttons.state(UP_LMT_SW)) where it kept moving while the state of the switch was not true, and simply changing it to while (Buttons.state(UP_LMT_SW)) where it would move as long as the state WAS true would work.... but it doesn't so I must be missing something simple somewhere

I'm going to write a smaller program version tomorrow and try all of these suggestions to see what's going on

Thanks again for the help, really appreciate it
M

So I've been trying different things this morning for a while (couple of cups of coffee now) and still no solution to what I'm doing wrong
I took this out of my program so it's only dealing with the home routine

#define MAX8BUTTONS     // saves ressources if less than 8 buttons needed

#include <MobaTools.h>
// 4 = step                 // Stepper driver pin connection
// 2 = direction            // Stepper driver pin connection


// 33 = home limit switch
// 35 = lower limit switch 


const byte buttonPins [] = {18, 19, 33, 35 };
enum : byte {UP_START, DOWN_STOP, UP_LMT_SW, LOW_LMT_SW };

const byte buttonCount = sizeof(buttonPins);

MoToButtons Buttons( buttonPins, buttonCount, 30, 50 ); 
// 500ms to distinguish short/long

const int DIR = 2;          // Stepper DIR Pin 2
const int STEP = 4;         // Stepper STEP Pin 4

bool home_state = 1;     // home switch tripped
bool HOMED = false;


long maxSpeed = 15000;    
int ramp_len = 400;       // 400~500 seems about as short as it can be
int StepSpeed = maxSpeed; // until the pot is checked

long nextPos = 0;             // Where to go to while moving

MoToStepper myStepper (400,STEPDIR);    // setps/rev and type of controller
MoToTimer stepperPause;                 // Pause between cycles


void setup()
{  
  Serial.begin(115200);
  
  myStepper.attach( STEP, DIR );      // 4, 2
  myStepper.setSpeed(StepSpeed );     // set at max initially
  myStepper.setRampLen(ramp_len);     // 400
  
  delay(2000);            // need to prevent partial run when loading 

  Buttons.processButtons();
  Home();                 // Home the machine upper limit
}


void loop()
{
  Buttons.processButtons();
}




void Home() // Home the machine to the upper limit switch 
{
  Buttons.processButtons();
  myStepper.setZero(0);               
  myStepper.setSpeed( 1500 );         // slow speed for homing
  
  // move the carrige down some to make sure it's off the switch
  myStepper.moveTo(1400); 
  while ( myStepper.distanceToGo()!=0 )
  {
    Serial.println("Staging");
    myStepper.moveTo(1400);   
  }

  while (HOMED == false )           // move up to home switch
   {
    //while (!Buttons.state(UP_LMT_SW))
    while (Buttons.state(UP_LMT_SW))
      {
        Serial.println("HOMING");
        Buttons.processButtons();
        myStepper.moveTo(myStepper.currentPosition()-400);    // keep moving up
      }
    myStepper.stop();
    myStepper.setZero(1200);          // come down off the switch
    myStepper.moveTo(0);
    HOMED=true;
    Serial.println("HOMED");
    
   }
}

When running, the program goes inside the while loop once, when it processes the buttons inside that while loop it now sees the state as 1 and exits the loop stopping the homing, the serial print goes from Staging to HOMING (once) and then HOMED. Even if the limit pin is connected directly to ground the whole time

I use the limit switches for safety during the rest of the program and check the states there as well (haven't tested this issue there yet, but I suspect it's going to be the same)

I'm obviously not using the library quite right :frowning:

M

Well, that's a bit true ... :wink: At least you don't use MoToStepper as it is intended to be used. But that should not be the reason for your actual problems. I'll have a closer look this evening and get back afterwards.

BTW: limit swiches don't need to be debounced. It could even be disruptive, because you detect the switching with a short delay. The first close ( or opening) of the switch means the limit position is reached. Further bouncing does not matter.

Changed the test program, eliminated the call in the setup to home function and made the loop

void loop()
{
  Buttons.processButtons();
  Serial.println(Buttons.state(UP_LMT_SW));
}

connected the pin (33) to ground and ran the program
There serial output goes
0 <<<<<<<<<<<<< this I believe is what's causing the while statement to not work right
0 <<<<<<<<<<<<<
0 <<<<<<<<<<<<<
1
1
1
2
2
2
3
3
3
etc

If I disconnect the pin (let it float) it prints 0,0,0,0,0,0,0,0

Looking at the .h I see that the state is actually "return _buttonTime[buttonNbr]" and not what I was expecting (0/1)

I'm trying to avoid a bunch of digitalReads of the limit switches within the program body which were slowing it down too much and that's why I was using the processbuttons.
I may have to just stick with the NO limit switches
The other switches and a couple of analog ones all seem to work fine

Let me know if you come up with any way around this
Thanks again for your time
M

Oh that doesn't sound good... I thought everything else seemed to be working pretty good.... damn
M

But it can be much simpler :smile: . I'll show you later.

Unfortunately the docs are not quite accurate in this case ( I changed the behaviour some versions ago, but obviously forgot to adjust the docs ). I have to correct the docs in the next release.
The comments in motoButtons.h are correct:

      byte state( uint8_t buttonNbr );       // get static state of button (debounced) 
                                                // 0 if button not pressed
                                                // presstime in debounce tics ( up to 255 ) if pressed

You could also use the release method instead of 'state'.
I'm now offline for some time :slightly_smiling_face:

Over complication as well as brute-force and ignorance are kind of specialties of mine :slight_smile:

I'll put it aside and work on some of the other bits for now until you can have another look see.
Again, many thanks

M

The unexpected return values shouldn't be the issue, since zero is false and non-zero is true.

Try this, and then with ! Buttons.state(UP_LMT_SW):

void loop()
{
  Buttons.processButtons();

  if (Buttons.state(UP_LMT_SW))
    Serial.println("one position");
  else
    Serial.println("                      the other position");
}

I have just done this with a hacked version of the example for buttons at the repository for the tools.

The return just skips the rest of the loop:

void loop() {
  //--------------------------------------------------------
  // Block "Eingabe": Taster entprellt einlesen und Startzeit merken
  Taster1.processButtons();
  // Ende Block "Eingabe"
  //--------------------------------------------------------
  
//  Serial.println(Taster1.state(0));

  if (!Taster1.state(0))
    Serial.println("one position");
  else
    Serial.println("                      the other position");

  return;

adding or removing the logical negation operator results in a change of the sense of the switch - all and what you are looking for.

Also - I get why you'd skip debouncing a limit switch, but looking at the code for buttons in the moba tools it will react as fast as possible, I think. You do have to call processButtons() which takes some time handling 16 buttons, but it looks like a react and ignore style of debouncing; I wonder how much faster, if you are sticking with any kind of polling, would be handling the limits switches directly.

Which would be very simple as debouncing wouldn't enter into it. It would still need frequent interrogation.

a7

At one point i tried something like that but since I needed it to loop inside a function I ended up with

Home=false
while (!home)
{
  {
    if (!Buttons.state(UP_LMT_SW))
    {
      Home=true
    }
    else
   {
    Buttons.processButtons();
    myStepper.moveTo(myStepper.currentPosition()-400);    // keep moving up
  }
}

Which kind of worked but not consistently and was getting really bloated even for me :slight_smile:

Hi,
I tried the your sketch of #8 on my hardware and detected the cause of your problem.
It's how MoToButtons works. It debounces the buttons by not reading them too often. The debounce time is defined as the 3rd parameter wenn creating the MoToButtons object - 30ms in your sketch. But the ESP doesn't need that time from the first processButtons() in setup to the next processButtons in Home(). So the button state is not yet set. A short delay ( greater than the debounce time ) in setup before starting Home will overcome this.
But this should not be needed if the button is pressed already when the program starts. I will correct this in the next release.

A question:

  myStepper.setSpeed(StepSpeed );     // set at max initially

You are aware that setSpeed() doesn't set the speed in steps/s, but in RPM?
You need to use setSpeedSteps() if you want to define the speed in steps/sec.

Here is your sketch from #8 with the delay() and deletion of some unneeded statements:

#define MAX8BUTTONS     // saves ressources if less than 8 buttons needed

#include <MobaTools.h>
// 4 = step                 // Stepper driver pin connection
// 2 = direction            // Stepper driver pin connection


// 33 = home limit switch
// 35 = lower limit switch


const byte buttonPins [] = {18, 19, 33, 35 };
enum : byte {UP_START, DOWN_STOP, UP_LMT_SW, LOW_LMT_SW };

const byte buttonCount = sizeof(buttonPins);

MoToButtons Buttons( buttonPins, buttonCount, 30, 50 );
// 500ms to distinguish short/long

const int DIR = 2;          // Stepper DIR Pin 2
const int STEP = 4;         // Stepper STEP Pin 4

bool home_state = 1;     // home switch tripped
bool HOMED = false;


long maxSpeed = 15000;
int ramp_len = 400;       // 400~500 seems about as short as it can be
int StepSpeed = maxSpeed; // until the pot is checked

long nextPos = 0;             // Where to go to while moving

MoToStepper myStepper (400, STEPDIR);   // setps/rev and type of controller
MoToTimer stepperPause;                 // Pause between cycles


void setup()
{
  Serial.begin(115200);

  myStepper.attach( STEP, DIR );      // 4, 2
  myStepper.setSpeed(StepSpeed );     // set at max initially
  myStepper.setRampLen(ramp_len);     // 400

  delay(2000);            // need to prevent partial run when loading

  Buttons.processButtons();
  delay(60); // to be shure at least one debounce time elapsed

  Home();                 // Home the machine upper limit
}


void loop()
{
  Buttons.processButtons();
}


void Home() // Home the machine to the upper limit switch
{
  Buttons.processButtons();
  myStepper.setSpeed( 1500 );         // slow speed for homing

  // move the carrige down some to make sure it's off the switch
  myStepper.move(1400);  // relative move, no matter where the stepper is located
  while ( myStepper.distanceToGo() != 0 )
  { // only wait untiol all steps are executed. 
    // No need to call move(To) again
  }
  myStepper.rotate(-1);
  //while (!Buttons.state(UP_LMT_SW))
  while (Buttons.state(UP_LMT_SW))
  { // wait until limit switch is reached
    Buttons.processButtons();
  }
  myStepper.stop();
  myStepper.setZero(1200);          // come down off the switch
  myStepper.moveTo(0);
  HOMED = true;
  Serial.println("HOMED");

}

I would check the limit switch in Home() directly with digitalRead().

OMG, that actually makes some sense and explains why it was reading a 0 on the first couple of loops through the while loop.

Made your changes and it now works here on the test rig I have on the bench as expected. (and the crowd goes wild)
I'll take it out and try it in the shop after supper, so glad that's working and I can move along

The location of the limit switch isn't supper critical in this application (it's +/- several mm with about 15mm before the hard stop but I could certainly change it to a digitalRead within the homing loop

Many, many thanks
M

The steps/rev ( 400 in your case ) is needed to be correct if you want to use rpm as stepper speed. Internally always steps/sec is used. The steps/rev is used to convert the rpm to steps/sec.
You can always set the speed as rpm ( with setSpeed ) or as steps/sec ( with setSpeedSteps ). But setSpeed will only set the speed accurate if the steps/rev are also set correct.

As long as you don't use Buttons.longPress(UP_START) or Buttons.shortPress(UP_START) the value for longpress has no meaning. Buttons.pressed(UP_START) is always set the moment that the button was pressed - reagardless of the value for longpress.

Yes, but it should not be if the button is pressed from the very beginning. It's on my ToDo list for the next release :wink: - so your sketch helps making MobaTools a bit better again :sunglasses:

Loaded it up onto the real machine in the shop and after some tweaking it works as it should. (I couldn't seem to use the enum name of the pin for the digitalRead for whatever reason)

Then I tried to get the second limit working but used pin34 and completely forgot about it needed a pullup resistor... but once that was added it's homing itself just fine.

Onward to the next problem :slight_smile:

That's because the enum name isn't really the pin number, but the index in the array of pin numbers. MoToButtons takes care about this and looks it up in the array by itself. digitalRead needs the pinnumber directly:
digitalRead(buttonPins[UP_LMT_SW]);

Yeah, not all GPIO's of the ESP32 have an internal pullup and are really 'general' IO's. That can be confusing ...