limit switch with ext pwr supply

I have a limit switch rated for 125VDC. I would like to supply it with 12VDC from a power supply and not from the Arduino 5V pin, will this work (see pic)?

Edit: It looks like Arduino Uno digital pins operate at 5v, so I would have to also put a L78S voltage regulator in-line with the digital pin?

if you are using the limit switch to control an arduino why not use the 5v unless your meaning proximity switch then use opto coupler 4n25

Or even easier, don't let it switch 5V nor 12V. Let it switch GND :wink:

I'm scrapping the idea to supply 12VDC to the limit switches... see below.

The project is to control 2 roll-up sides of a greenhouse UP/DOWN. Since I'll be using a 12VDC 36Amp power supply for this project to power the 2x32Amp motor driver, I figured I would use the logic +5v output off the driver to power the switches. Hence, my "external power supply" question.

Attached the schematic so far.

2 SPDT momentary (center off, normally open) switches. I need to detect UP, DOWN position of each switch, so I used two I/O pins for each switch. Each I/O pin on the switch is held HIGH via a 10K resistor. When the switch gets closed, (depending on the direction), one of the I/O pins will go LOW.

The TOP/BOTTOM of each roll-up side will have a limit switch to tell me when the side is fully open or closed. Each I/O pin wired to the NC limit switches will be held LOW, then when opened go HIGH via a 10K resistor.

I'm a complete newbie. Am I on the right track so far or are there serious problems with the design?
Next step will be to add the motor driver to the schematic. It's a Sabertooth 2x32A which will only require one I/O pin.

Untitled Sketch_bb.pdf (1.03 MB)

Seems right to me. Keep in mind the Arduino has internal pull up resistors as well which saves you from adding real resistors. But that said, they are more in the range of 30k and a lower value pull up resistor can help to reduce noise if the wires are longer. For somewhat longer wires I even switch to 1k.

And a nice exercise for the next time, try to draw a schematic instead of a Frizing breadboard view. Once you get the hang of them they are wayyyyy easier to read then Fritzing breadboard views (understatement). Fritzing can make a real schematic as well :wink:

Here's my approach to button control of motors 1 & 2; whattya think?

I have to update safeToMove() fcn to use the new 2D array approach. I started down the path of separate variables for each button, direction, motor, ect.. and it got super messy.

I get the rest of my hardware tomorrow, hopefully can give this a run outside of the IDE :slight_smile:

#include <SabertoothSimplified.h>
SabertoothSimplified ST;

// Connections to make from arduino to sabertooth:
//   Arduino TX->1  ->  Sabertooth S1
//   Arduino GND    ->  Sabertooth 0V

//state declarations
int motorState[2][1]; //2 rows, 1 columns each. {motor1{currentState}, motor2{currentState}}; -1=REV, 0=OFF, 1=FWD
int buttonUpState[2][2]; //{{0,1},{0,1}} -->2 rows, 2 columns each. {switch1{current, previous}, switch2{current,previous}}
int buttonDnState[2][2];
int limitUpState[2][2];
int limitDnState[2][2];

//pin declarations
int buttonUpPin[2] = {2,4}; //{switch1-pin,switch2-pin}
int buttonDnPin[2] = {3,5};
int limitUpPin[2] = {9,7};
int limitDnPin[2] = {8,6};

void setup()
{
  for (int i = 0; i < 2; i++) {
    pinMode(buttonUpPin[i],INPUT);
    pinMode(buttonDnPin[i],INPUT);
    pinMode(limitUpPin[i],INPUT);
    pinMode(limitDnPin[i],INPUT);
    ST.motor(i+1,0); //stop motor
    motorState[i][0] = 0; //set motor state to stopped
  }
  updateCurrentStates();
  updatePrevStates();  
  SabertoothTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.  
}

//functions
/*bool safeToMove(int motorNum, int dirByte){
  bool result;
  if (motorNum == 1){ //motor1
    if (motor1State == 0){ //motor currently stopped
      if (dirByte > 0){ //move UP/fwd
        if (digitalRead(motor1Ulm)==HIGH){ //motor1 upper limit HIGH, means curtain is fully open
          result == false;
        }
        else{
          result == true;
        }
      }    
      else{ //move DOWN/reverse
        if (digitalRead(motor1Dlm)==HIGH){ //motor1 down limit HIGH, means curtain is fully closed
          result == false;
        }
        else{
          result == true;
        }
      }
    }
    else{ //motor currently moving in fwd or rev
      result = false;
    }
  }
  return result;
}
*/

void updateCurrentStates(){
  for (int i = 0; i < 2; i++) {
    //{{0,1},{0,1}} -->2 rows, 2 columns each
    buttonUpState[idex][0] = digitalRead(buttonUpPin[i]); //update first column of each row with current state    
    buttonDnState[idex][0] = digitalRead(buttonDnPin[i]);
    limitUpState[idex][0] = digitalRead(limitUpPin[i]);
    limitDnState[idex][0] = digitalRead(limitDnPin[i]);
  }
}

void updatePrevStates(){
  for (int i = 0; i < 2; i++) {
    //{{0,1},{0,1}} -->2 rows, 2 columns each
    buttonUpState[idex][1] = buttonUpState[idex][0]; =//update 2nd column of each row with current state    
    buttonDnState[idex][1] = buttonDnState[idex][0];
    limitUpState[idex][1] = limitUpState[idex][0];
    limitDnState[idex][1] = limitDnState[idex][0];
  }
}

void loop()
{
  updateCurrentStates(); //update states of all buttons and limits
  for (int i = 0; i < 2; i++) {
    if(limitUpState[idex][0] != limitUpState[idex][1]){
      if(limitUpState[idex][0]==HIGH){//this limit-UP was TRIGGERED
        ST.motor(i,0);
        motorState[i][0] = 0;
      }
    }
    if(limitDnState[idex][0] != limitDnState[idex][1]){
      if(limitDnState[idex][0]==HIGH){//this limit-UP was TRIGGERED
        ST.motor(i,0);
        motorState[i][0] = 0;
      }
    }
    if(buttonUpState[idex][0] != buttonUpState[idex][1]){
      if(buttonUpState[idex][0]==LOW){//this button-UP went from OFF to ON
        if(safeToMove(i,127)){
          ST.motor(i,127);
          motorState[i][0] = 1;
        }
      }
      else{//this button-UP went from ON to OFF
        ST.motor(i,0);
        motorState[i][0] = 0;
      }
    }
    if(buttonDnState[idex][0] != buttonDnState[idex][1]){
      if(buttonDnState[idex][0]==LOW){//this button-DOWN went from OFF to ON
        if(safeToMove(i,-127)){
          ST.motor(i,-127);
          motorState[i][0] = -1;
        }
      }
      else{//this button-DOWN went from ON to OFF
        ST.motor(i,0);
        motorState[i][0] = 0;
      }
    }
  }
  updatePrevStates();
}

First thing I read:

int motorState[2][1];

Really? ::slight_smile:

And you can save halve the memory if you use bytes to store all the states (or (signed) char is you want signed).

And I'm just a big fan of handling switches in a library. Saves you work and saves you a lot of clutter.

And why save the motor state? You never use it. And can't the motor library supply the state? Now it's up to you to keep the real motor state and your state variable in sync...

And I would say it would make sens to put the up and down versions in a array as well so you can loop over it instead having to do almost the same for up and down.

go easy now; I'm a C++ newb. Makes sense tho, I'll switch to char

A didnt' think to look for a button library, something like this? Arduino Playground - Button Library

motorState -- If a motor is commanded to move FWD during 1st iter of loop(), I dont' want to command it to move fwd again during 2nd iter of loop() if the button is still pressed. So I want to check the motorState and if it's moving, don't command it to move again. Maybe I'm not right?
The <SabertoothSimplified.h> library does include get() methods I could call to retrieve motorState. It would just req a wire from Rx to the driver and use it rather then keeping track of it myself.

again, I'm a newb, could you give me a pointer of how to loop over 2 variables?

int limitUpState[2][2];
int limitDnState[2][2];

//can I load them into an array and loop over the array?
//something like this?
int foo[2] = {limitUpState,limitDnState};
for (int i = 0; i < 2; i++) {
  int limitVar = foo[i];
  for (int j = 0; j<2; j++){    
    if (limitVar[j][0] != limitVar[j][1]){
      ..........
    }
  }
}

I'm a fan of Bounce2 library :slight_smile: But there are many all with pro's and cons.

Yeah, I saw about the library. Pretty crappy not to include a read :confused: But then, it also uses a int for values between -127 and 127...

But this is what I would make of it

#include <SabertoothSimplified.h>
#include <Bounce2.h>

// Connections to make from arduino to sabertooth:
//   Arduino TX->1  ->  Sabertooth S1
//   Arduino GND    ->  Sabertooth 0V

//pin declarations
const byte ButtonPins[][2] = {{2, 3}, {4, 5}};
const byte LimitPins[][2] = {{9, 8}, {7, 6}};

const byte NumberOfControls = sizeof(ButtonPins)/sizeof(ButtonPins[0]);

enum pindir_t{UP, DOWN}; //just for easy access but we don' even use it...

//state declarations
signed char setMotorStates[NumberOfControls];
signed char sendMotorStates[NumberOfControls];

Bounce buttons[NumberOfControls][sizeof(ButtonPins[0])];
Bounce limitSwitches[NumberOfControls][sizeof(ButtonPins[0])];
//SabertoothSimplified motorController;

void setup(){
  SabertoothTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches. 
  Serial.begin(115200);
  
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].attach(ButtonPins[i][j], INPUT_PULLUP);
      limitSwitches[i][j].attach(LimitPins[i][j], INPUT_PULLUP);
    }
    
    motorController.motor(i +1, 0);
  }
}


void loop(){
  updateAllSwitches();
  
  for(byte i = 0; i < NumberOfControls; i++){    
    for(byte j = 0; j < ButtonPins[0]; j++){
      //check limit switches
      if(limitSwitches[i][j].fell()){
        setMotorStates[i] = 0;
      }
      
      //direction button pressed and not at limit
      if(buttons[i][j].fell() && limitSwitches[i][j].read()){
        setMotorStates[i] = (j ? -127 : 127);
      }
    }
    
    //now update the motor if we need to
    //because this is the ONLY place we allow to call the motor controller the state varaibles can' get out of sync
    if(sendMotorStates[i] != setMotorStates[i]){
      sendMotorStates[i] = setMotorStates[i];
      motorController.motor(i + 1, sendMotorStates[i]);
    }
  }
}

void updateAllSwitches(){
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].update();
    }
  }
}

I have some studying to do, thank you!

The library doesn't have the ability to GET motor states, but it can be done via serial cmds, see here p25: https://www.dimensionengineering.com/datasheets/Sabertooth2x32.pdf.

Seems like it would be cleaner to choose either Library or Serial cmds, and not mix them.?

septillion:
I'm a fan of Bounce2 library :slight_smile: But there are many all with pro's and cons.

Yeah, I saw about the library. Pretty crappy not to include a read :confused: But then, it also uses a int for values between -127 and 127...

But this is what I would make of it

#include <SabertoothSimplified.h>

#include <Bounce2.h>

// Connections to make from arduino to sabertooth:
//  Arduino TX->1  ->  Sabertooth S1
//  Arduino GND    ->  Sabertooth 0V

//pin declarations
const byte ButtonPins[][2] = {{2, 3}, {4, 5}};
const byte LimitPins[][2] = {{9, 8}, {7, 6}};

const byte NumberOfControls = sizeof(ButtonPins)/sizeof(ButtonPins[0]);

enum pindir_t{UP, DOWN}; //just for easy access but we don' even use it...

//state declarations
signed char setMotorStates[NumberOfControls];
signed char sendMotorStates[NumberOfControls];

Bounce buttons[NumberOfControls][sizeof(ButtonPins[0])];
Bounce limitSwitches[NumberOfControls][sizeof(ButtonPins[0])];
//SabertoothSimplified motorController;

void setup(){
  SabertoothTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  Serial.begin(115200);
 
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].attach(ButtonPins[i][j], INPUT_PULLUP);
      limitSwitches[i][j].attach(LimitPins[i][j], INPUT_PULLUP);
    }
   
    motorController.motor(i +1, 0);
  }
}

void loop(){
  updateAllSwitches();
 
  for(byte i = 0; i < NumberOfControls; i++){   
    for(byte j = 0; j < ButtonPins[0]; j++){
      //check limit switches
      if(limitSwitches[i][j].fell()){
        setMotorStates[i] = 0;
      }
     
      //direction button pressed and not at limit
      if(buttons[i][j].fell() && limitSwitches[i][j].read()){
        setMotorStates[i] = (j ? -127 : 127);
      }
    }
   
    //now update the motor if we need to
    //because this is the ONLY place we allow to call the motor controller the state varaibles can' get out of sync
    if(sendMotorStates[i] != setMotorStates[i]){
      sendMotorStates[i] = setMotorStates[i];
      motorController.motor(i + 1, sendMotorStates[i]);
    }
  }
}

void updateAllSwitches(){
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].update();
    }
  }
}

first question, why do you use type "byte" and not "char" ?

Because I've never seen negative pin numbering :smiley: But indeed, it doesn't really matter. But when I don't need a signed type (although the signed version would also fit) I use the unsigned version.

Few more questions:

  1. shouldn't we also update the limitSwitches in the fcn?
void updateAllSwitches(){
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].update();
      limitSwitches[i][j].update(); //<--update state of LimitSwitches prior to reading them in loop()
    }
  }
  1. see comments below in code
  updateAllSwitches();
  
  for(byte i = 0; i < NumberOfControls; i++){   
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      //check limit switches
      if(limitSwitches[i][j].rose()){ //changed to .rose(), limitSwitches are NC and held low
        setMotorStates[i] = 0; 
      }
     
      //direction button pressed and not at limit
      if(buttons[i][j].fell() && limitSwitches[i][j].read()){ //limitSwitches[i][j].read()=LOW, does this equate to true? I can't find it in the Bounce2 docs.
        setMotorStates[i] = (j ? -127 : 127); //is this correct? if j==1 then setMotorStates[i] = -127, if j==0 setMotorStates[i] = 127 
      }
    }
  1. You are absolutly right! Need to leave some work for you 0:-)

  2. If they are NC you indeed need to flip some logic.

limitSwitches[i][j].read()=LOW, does this equate to true? I can't find it in the Bounce2 docs.

No, that will throw an error because you can't set a dunction :smiley: But if you did mean "==", depends on what the state of the switch is. Remember true, HIGH and 1 all do the same. Same for false, LOW and 0.

So to flip the logic here you need to just invert the .read()

if(buttons[i][j].fell() && !limitSwitches[i][j].read()){ //note the !, == LOW would do the same.

But because they are NC it's pretty normal (and easy) to just inline them with the motor and just to put a diode (in the correct direction that it can spin away from the limit switch) across the contacts. Fool proof, no software, takes no pins on the uC... Only downside, the software can't show (display, send out etc) if it's indeed fully up or down.

//is this correct? if j==1 then setMotorStates = -127, if j==0 setMotorStates = 127
[/quote]
It should be. I define the 0 element to be the UP version and 1 the down. So pin 2 and 4 are the up buttons, and 9 and 7 the up limit switches.

Thanks for the hand holding, Im learning quite a bit.

I realized that there wasnt a condition for button release (momentary switches) and no limit, so I edited it as follows. Havent tested with hardware yet but emulators worked.

if((buttons[i][j].fell() || buttons[i][j].rose())  && !limitSwitches[i][j].read()){
        setMotorStates[i] = (buttons[i][j].fell() ? (j ? -30 : 30) : 0);
      }

Next phase of this project is to incorporate with existing temperature reading ethernet shield. A server as quick/basic UI with 2 HTML buttons and up/down status indicator divs.

But why do you want an action on button release? You want it to run slower with the switch released? Why?

I just had the 30, -30 vals in there for testing. At 127 speed the motors would jump around the work bench when they start/stop.

The sides will take 10 sec. Or so to roll fully open. A lot of time ill want them only open a few inches so a momentary switch will allow me to have better control.

But, in reality ill be using the ethernet UI most of the time to open/close when im not at home. Like I said, that will take some more thought. I just want to get the basics working now tho.

Ahh, yes. I wrote the program to automatically fully open or close the blinds. But it would not make sense to add the button.rose() to do the same as button.fell()... You want to rise to stop the motors. Small edit:

#include <SabertoothSimplified.h>
#include <Bounce2.h>

// Connections to make from arduino to sabertooth:
//   Arduino TX->1  ->  Sabertooth S1
//   Arduino GND    ->  Sabertooth 0V

//pin declarations
const byte ButtonPins[][2] = {{2, 3}, {4, 5}};
const byte LimitPins[][2] = {{9, 8}, {7, 6}};

//motor speed
const signed char MotorSpeed = 30;

const byte NumberOfControls = sizeof(ButtonPins)/sizeof(ButtonPins[0]);

enum pindir_t{UP, DOWN}; //just for easy access but we don' even use it...

//state declarations
signed char setMotorStates[NumberOfControls];
signed char sendMotorStates[NumberOfControls];

Bounce buttons[NumberOfControls][sizeof(ButtonPins[0])];
//limit switches NC active low => active high
Bounce limitSwitches[NumberOfControls][sizeof(ButtonPins[0])];
//SabertoothSimplified motorController;

void setup(){
  SabertoothTXPinSerial.begin(9600); // This is the baud rate you chose with the DIP switches.
  Serial.begin(115200);
  
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].attach(ButtonPins[i][j], INPUT_PULLUP);
      limitSwitches[i][j].attach(LimitPins[i][j], INPUT_PULLUP);
    }
    
    motorController.motor(i + 1, 0);
  }
}


void loop(){
  updateAllSwitches();
  
  for(byte i = 0; i < NumberOfControls; i++){    
    for(byte j = 0; j < ButtonPins[0]; j++){
      //check limit switches or button is released
      if(limitSwitches[i][j].rose() || buttons.rose()){
        setMotorStates[i] = 0;
      }
      
      //direction button pressed and not at limit
      if(buttons[i][j].fell() && !limitSwitches[i][j].read()){
        setMotorStates[i] = (j ? -MotorSpeed : MotorSpeed);
      }
    }
    
    //now update the motor if we need to
    //because this is the ONLY place we allow to call the motor controller the state varaibles can' get out of sync
    if(sendMotorStates[i] != setMotorStates[i]){
      sendMotorStates[i] = setMotorStates[i];
      motorController.motor(i + 1, sendMotorStates[i]);
    }
  }
}

void updateAllSwitches(){
  for(byte i = 0; i < NumberOfControls; i++){
    for(byte j = 0; j < sizeof(ButtonPins[0]); j++){
      buttons[i][j].update();
      limitSwitches[i][j].update();
    }
  }
}

that makes sense and is cleaner then nesting 2 ternary operators.
Everything is working on the manual side (manual meaning buttons/limits).

Onto URL GET control of the motors.

My goal is to control both motors using URL GET commands, example:
http://hostip?motor1=UP&duration=2
http://hostip?motor1=FULLCLOSED
http://hostip?motor1=FULLOPEN
ect...

Do you think it's best to use 2 Arduino's, rather then clobbering the current sketch with URL parsing?

Arduino1
Add ethernet shield
parses URL GET commands
sends the parsed data to Arduino2
setup as I2C master
sends motor/limit status back to URL client

Arduino2
polls status of buttons and limits, commands motors
setup as I2C slave (to receive URL parsed data)
listens for URL parsed data, commands motors
relays status of motors/limits back to Arduino1

Or just add the URL parsing and ethernet shield to the sketch that's started?

I would say two Arduino's is complete overkill. With all the libraries getting the command via I2C or Ethernet takes about the same amount of code. And the Arduio will do nothing for 99% of the time. Just add code to do the URL parsing and set a flag correspondingly and at triggers to start and stop depending of those flags.