Change functionality of mechanical switches

darkenney:
Another personal convention I have is putting a lowercase f to start all my custom functions - e.g. fdecreaseSpeed. It makes it very easy for me to spot these in a sea of code. Would you like to call it ‘terrible’ now or wait until I post something again down the road?

Not a bad idea at all.

It is indeed surprising that a pin set to INPUT_PULLUP would vary that much. I wonder what other equipment you have nearby?

Function name that way is not bad. I'd just make the second letter an upper case as well for easy reading.

darkenney:
When I switched to analogRead and pin A0, I was shocked by the range in values and the amount of environment interference. Still so surprised I’m looking into the environment further.

Still very much sounds like a floating pin.

Actually I don't know what happens if you enable the internal pull-up on A0 using INPUT_PULLUP, but then do an analogRead() on it. I wouldn't be surprised pull-up resistor is switched off by that function, as it normally does not make sense at all to have the internal pull-up enabled on an analog input as it messes with the voltage you want to read.

If you want to be sure, add an external pull-up resistor of 20-30k, equivalent to the built-in ones.

wvmarle:
Actually I don't know what happens if you enable the internal pull-up on A0 using INPUT_PULLUP, but then do an analogRead() on it. I wouldn't be surprised pull-up resistor is switched off by that function, as it normally does not make sense at all to have the internal pull-up enabled on an analog input as it messes with the voltage you want to read.

I haven't tried it, but the pull-up should not be disabled. There is simply no reason for analogRead() to mess with the pin settings any more than digitalRead() does and it should not.

Even if it did, it should only change the DDR, not the output register. An interesting point there - the later version of pinMode in the IDE when INPUT_PULLUP was introduced writes to (sets) the output register when INPUT_PULLUP is chosen, but does it also clear it when INPUT is specified? Should it? I would say it should not as this would then be incompatible with the earlier version of the IDE.

And it makes excellent sense to use INPUT_PULLUP on an analog input, for example as a really simple way of reading a LDR or even thermistor. :grinning:

Paul__B:
An interesting point there - the later version of pinMode in the IDE when INPUT_PULLUP was introduced writes to (sets) the output register when INPUT_PULLUP is chosen, but does it also clear it when INPUT is specified? Should it? I would say it should not as this would then be incompatible with the earlier version of the IDE.

In that case: how could one switch off the pull-up resistor?
The old digitalWrite (inputpin, LOW) call for switching it off is not supposed to be used in the current IDE.

wvmarle:
The old digitalWrite (inputpin, LOW) call for switching it off is not supposed to be used in the current IDE.

Says who?

You are kidding! What complete nonsense! :astonished:


OK, I do note from this page

Additionally, the INPUT mode explicitly disables the internal pullups.

which seems like a particularly bad idea.

In any case, there really is no need to use pinMode() other than in setup() except perhaps to implement an "open-drain" function where you can admittedly use it to toggle cleanly between INPUT and OUTPUT. It then becomes a nuisance if you wanted to toggle an open-drain but use the internal pull-up.

Paul__B:
Says who?

You are kidding! What complete nonsense! :astonished:

That's what I learned in this forum. Old method, don't use. Doing a digitalWrite() to an INPUT does sound a bit odd (though there are admittedly odder things in the Arduino world).

Sorry if I have this wrong but I'm a bit confused about the wiring.

Is the button connected to the opto on the Arduino side?
It should be on a pin not connected to the opto.
The connection should be in a code block that watches the button not just get pressed but count time as well.
Another code block should note how long the button is pressed and pulse the opto a number of times to match.

I make dirty "cheater switches" by moding sense pins as INPUT_PULLUP then sticking jumpers in the holes.
To "press" the button, I ground the jumper. That's it, the wiring is the easy part and I have debounce code.

PS just BTW: I'd give the user a slider or turn pot instead of 2 buttons. User can see the control position with either.

wvmarle:
That's what I learned in this forum. Old method, don't use. Doing a digitalWrite() to an INPUT does sound a bit odd (though there are admittedly odder things in the Arduino world).

Old method, still perfectly valid btw.

Arduino digitalWrite() sets a PORTx bit high or low. That is all it ever did. No magic, it changes PORT register bits.

Default pin state at startup is INPUT LOW.

The Arduino pinMode( x, INPUT_PULLUP ) command is a convenience that sets PORT and DDR registers.
Isn't it a bit late to expect Arduino to not have get-arounds and exceptions? It's a C/C++ compiler written in JAVA.

As to what setMode( x, INPUT ) does...

const byte testPin = 6;
const byte readPin = 7;

void hitEnter()
{
  while ( Serial.available() == 0 );  // blocks until serial has data
  while ( Serial.available() > 0 )  Serial.read();  // blocks until serial has no data
}


void setup() 
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\nTesting modes on pin 6 reading on pin 7\n" ));

  Serial.println( F( "Jumper pin 6 to pin 7 and hit the Enter key" ));
  hitEnter();
  pinMode( readPin, OUTPUT ); // pin is OUTPUT LOW
  delay( 10 ); // drain the wire
  pinMode( readPin, INPUT ); // pin is INPUT LOW
  Serial.print( F( "Pin 6 is INPUT, pin 7 reads " ));
  Serial.println( digitalRead( readPin ));

  Serial.println( F( "Hit the Enter key again" ));
  hitEnter();
  pinMode( readPin, OUTPUT ); // pin is OUTPUT LOW
  delay( 10 ); // drain the wire
  pinMode( readPin, INPUT ); // pin is INPUT LOW
  pinMode( testPin, INPUT_PULLUP );
  delay( 10 ); // fill the wire
  Serial.print( F( "Pin 6 is INPUT_PULLUP, pin 7 reads " ));
  Serial.println( digitalRead( readPin ));

  Serial.println( F( "Hit the Enter key again" ));
  hitEnter();
  pinMode( testPin, INPUT );  // let's see if moding as INPUT sets the PORT bit
  pinMode( readPin, OUTPUT ); // pin is OUTPUT LOW
  delay( 10 ); // drain the wire
  pinMode( readPin, INPUT ); // pin is INPUT LOW
  delay( 10 ); // fill the wire?
  Serial.print( F( "Pin 6 is INPUT_PULLUP, pin 7 reads " ));
  Serial.println( digitalRead( readPin ));
  
}

void loop() {
  // put your main code here, to run repeatedly:
}

Output:

Testing modes on pin 6 reading on pin 7

Jumper pin 6 to pin 7 and hit the Enter key
Pin 6 is INPUT, pin 7 reads 0
Hit the Enter key again
Pin 6 is INPUT_PULLUP, pin 7 reads 1
Hit the Enter key again
Pin 6 is INPUT_PULLUP, pin 7 reads 0

GoForSmoke, thanks for jumping in and going through the whole thread.

GoForSmoke:
Is the button connected to the opto on the Arduino side

No, it isn't. The button is on the Arduino side, but it's not connected to the opto.

GoForSmoke:
It should be on a pin not connected to the opto.

Yes, it is.

GoForSmoke:
The connection should be in a code block that watches the button not just get pressed but count time as well.
Another code block should note how long the button is pressed and pulse the opto a number of times to match.

That's not the way it's set up currently, but I do like that I idea. Although, I don't want to get into a situation where I need to wait until the button is done being pressed to say 'oh, button pressed for three seconds, pulse the the opto xx many times'. So, I'd have to think it through a bit, and that code might get tricky for me.

GoForSmoke:
PS just BTW: I'd give the user a slider or turn pot instead of 2 buttons. User can see the control position with either.

For this application, two buttons is better. I had experimented with a y-n-y momentary rocker to replace the two switches. Thanks again.

darkenney:
That's not the way it's set up currently, but I do like that I idea. Although, I don't want to get into a situation where I need to wait until the button is done being pressed to say 'oh, button pressed for three seconds, pulse the the opto xx many times'. So, I'd have to think it through a bit, and that code might get tricky for me.

The trick is explained in web sites, a thread here and in the Arduino Built-In Example BlinkWithoutDelay where you mostly figure it out yourself.

If your code doesn't use delay() or other means to stay and wait for something while running nothing else but interrupts (which are NOT the answer, they have overhead and issues of their own) then it will be able to make multiple things happen at the same time smoothly.

How to see if an interval is still running and do something with a what to do if time is up:

if ( millis() - startMillis < desiredInterval )  // if my wait time is not up yet
{
    // check the button task status and do something on change
}
else  // wait is over
{
    // change a state value so the sketch quits looking
}

When you code you are describing how to do something. You will get better with practice.

Thank you! Coincidentally, I had just come across blink without Delay() during the search for an approach. Implementing it here worked out well! I've been Delay() free now for over 75 minutes.

Also coincidentally, it's a critical precursor for what I need to do next on this project - which is create a speedometer from a reed switch input. (Those buttons increase and decrease the speed of a DC motor.)

The code for this is proving tougher. For a single revolution, the magnet takes a small amount of time to pass the reed sensor, so the input pin reads, not as a single LOW but, as a string of LOWs as it's going past. There's also the bounce of the reed switch adding some misleading HIGHS into those LOWs, and there's a lot of interference here - probably from the dc motor; so, there are more incorrect HIGHs and LOWs scattered throughout. Edge detection, filtering, I'm thinking that I still won't need interrupts since updating the mph display would be in the loop of pressing and holding the speed buttons.

At the highest speed I need to measure with the reed switch (about 1,300 rpm equaling 12mph), a full revolution would take 45 milliseconds. (I wouldn't be able to get a Hall effect Sensor into the setup.) I found that without Serial.printing individual vales, the Arduino can repeat the loop checking the magnet state about 200,000 times a second. That would give me 900 inputs in the span of 45 milliseconds to analyze in order to determine speed. However, when I output through Serial.print for troubleshooting, it will output significantly less than that leaving me looking at a collection of HIGHs and LOWs that isn't definitive.

Since this next issue is no longer related to my mechanical switches, I'll start a new thread somewhere else in the forum.

Now, make that over 80 minutes Delay() free.

I think that you are supposed to keep everything in the project in 1 thread for the reason that members searching the forum for solutions find all the info in one place.

You're getting to where you are coding for events in time. It's GREAT that you can get multiple closed reads through the reed switch, your sensing is fast enough to be solid!

You don't want to go by the pin state but rather by changes in the pin state and this is where finite state machine style coding comes in as key #2 in real world automating. With a state machine you track and control the progress of your process through a variable and most often a switch-case statement that has all the steps your process needs with that state variable as the means to switch from one step to another (not confined to the next), do something until finished in one case then change the state to run a different case --- the description is general/non-specific because the technique is, it can stretch to do incredible things where you have state machines working inside of state machines to analyze text and other complex things.

Those Nick Gammon tutorial links in my sig space, the 2nd one has a very nice State Machine example that reads formatted serial input as user control info, check it out.

Watch events in time and control events over time.

I'm a bit stuck on the next level of my speedometer code. Any guidance would be much appreciated.

I've got a reed switch with magnet on a wheel. The goal is to Serial.print the speed in mph every .5 seconds to 1 decimal place. At the slowest speed of the wheel, I’ll need about 8 seconds worth of input to resolve to .1 mph. Make sense?

Right now I just have a single 8 second loop, followed by another, followed by another rather than -- I guess -- 16 of them going at once --

So two things with the code - 1) I don't have the data types correct so mph comes out as 0

11:15:24.106 -> number of revolutions in this 8 seconds is 20; mph is 0
11:15:32.125 -> number of revolutions in this 8 seconds is 20; mph is 0

  1. An approach to get a most recent 8 second update every .5 second.

Again, this code is just the 8 second loop, but it seems to work except for the mph calculation. Just some direction would be helpful. Thank you!

const int reedPin = A2; //the analog inpout pin that will receive the data from the reed switch
const int interval = 8000; //the number of millis needed to see an edge (revolution) at slow speeds
const long multiplier = 0.069691051; //multiple the number of edges (revolutions) by this number to get mph
long mph =0.0; //final calc of mph is revoultions x multiplier 
int firstInput = 1;  //variable to get a reading from the reed switch
int secondInput = 1; //varaible to get a second reading from the read switch
int edges = 0;  //variable that will count the number of edges in the 8 second timefrome
unsigned long startMillis = 0; //will hold the millis of the start of the 8 second interval
unsigned long currentMillis = 0; //will get updated with the current millis to check if we're still in the 8 seconds
 
 void setup() {
  pinMode(reedPin, INPUT_PULLUP);
  Serial.begin(9600); //troubleshooting has been challenging as output to screen slows down capture of inputs
  startMillis = millis(); //with such a long inteval want the first loop to be mostly valid
}

void loop(){
  currentMillis = millis(); //as we loop keep updating the currentMillis with the correct time

  if(currentMillis - startMillis <= interval) { //if we're still in the 8 second time frame...
    firstInput = analogRead(reedPin); //get a reading from the reed pin
    if(firstInput < 130){firstInput = 0;} //if it's low enough make it 0
    
    delay(4); //pause very briefly
   
    secondInput = analogRead(reedPin); //get a second reading from the reed pin
    if(secondInput < 130){secondInput = 0;} //if it's low enough make it 0
    
    if(firstInput !=0 && secondInput ==0){edges++;}//if the first input is not 0 and the second input is
                                          //we've scored ourselves and edge, which is the same as a revolution
    }
    else {//we're no longer in the 8 second loop so print out and reset
          mph = edges * multiplier;
          Serial.print ("number of revolutions in this 8 seconds is   ");
          Serial.print (edges);
          Serial.print (";   mph is    ");
          Serial.println (mph);
          edges = 0; //reset edges
          startMillis = millis(); //restart the 8 second counter
    
  }
                                                
}

Get more reads per revolution. Lots of ways to do that without multiplying magnets required, know what a gear tooth counter is?

You take the wrong approach.

Don't time how many rotations you get in 8 seconds, instead time how long it takes for a single rotation. Or, when it rotates faster, how long for 2 or more rotations.

Secondly, don't get yourself stuck in a loop waiting for the next edge. Just check every time loop() runs; and update the speed every 500 ms to reflect the latest measurement.

Thanks. Yes, makes sense. I tried some versions of that but I couldn’t get it right. I’ll go back and have a look at that approach again. A revolution at the slowest speed takes about 6 seconds, and at the highest speed, it’s 45 milliseconds.

For now, I made it its own function and call it at 1/2 second intervals when needed. It’s nicely updating a 3 digit 7 segment display.

There’s an odometer component so I also need to keep a count of total wheel revolutions. Thanks again.