internal pullup resistors & digitalWrite

it is more consistent with previous code

Hi Ray, could you clarify what previous code the three element version is consistent with?

Pins can be defined as output can benefit from a pullups too.

Pull-ups can only be enabled on inputs. Because of the way internal pull-ups are implemented on the controller chip, an output pin whose value is low cannot have pull-ups enabled.

My thoughts were to keep the pin variable consistant, to what its purpose has been in the past. A third variable would indicate that we were doing something different than what we had done in the past.

I have programmed other Atmel chips on their platform and I could have sworn that I set output pullups... I might be wrong, but it does seem that we can't do it on the mega 168/328. Sorry for the confussion but I was quite sure they could be set.

Because the outputs can't be set it may just make more sense to set up the function for the 2 variable inputs as suggested.

Coding Badly, I like your way of putting it. We could think of pins as having three modes: OUTPUT, INPUT, and PULLUP. In this case, would INPUT mode explicitly disable the pullup?

In this case, would INPUT mode explicitly disable the pullup?

I would think so. Otherwise, how would you disable them? :-?

In this case, would INPUT mode explicitly disable the pullup?

I also think this is easy to understand:

INPUT (input mode without pull-up)
INPUT_PULLUP (input mode with pull-up on)
OUTPUT (output mode)

If INPUT turns off the internal pullup, the risk is that existing code will break.

I believe there are two cases that would fail if INPUT disables the pullup...

  1. The user assumed pinMode would not alter the pullup.

  2. The user first called digitalWrite( pin, HIGH ) to enable the pullup then called pinMode( pin, INPUT ) to ensure the pin is configured as an input.

I've searched and searched the web site and the forum and I cannot find a single instance of either of these cases. I cannot find any existing code that would break.

I agree with the two above. INPUT should disable the internal pullup. That appears to be consistent with what users currently expect.

Coding Badly: thanks for checking that.

Also, I'd like to step back a bit and ask a question to those of you who think the current system is confusing. I was talking to Leah (Buechley, creator of the LilyPad and my advisor) about this and we both have the sense that pullup resistors are tricky to explain to people, but that the actual syntax to enable them is not as much of a problem.

Is this something we might better address with improved documentation explaining the existence and functionality of pullup resistors rather than changing the syntax for enabling them?

I think it needs both.

Certainly the docs could do a better job of explaining the whole concept and implementation in the Arduino system.

But I also think that the current mechanism is non-intuitive. It's not so much syntactically confusing as it is semantically disconnected. It is a mode in which you want the pin to behave, and the way you enable that mode is with a command (digitalWrite) that has nothing to do with the mode of a pin, but is otherwise used to send data out on it. We've overloaded the semantics of digitalWrite with another command that changes the mode.

Does that make sense?

That's why I favor some solution that uses a pinMode command to accomplish this.

.andy

I think its a combination of the two things as well. Internal pull ups are not obvious and need explanation, but the code that enables them at the moment tends to look like a coding error to a beginner rather than something that has a clear purpose even if you are not sure what - without a comment you simply wouldn't know by looking at the code - alot of other arduino syntax you can guess what it is doing and have a good chance of being close.

For someone who is in a class they get the explanation, and in time it often makes sense, but for someone who is working alone and using the documentation it may be harder. The pages explaining pullups are heavy on text with no diagrams or pictures, so look more intimidating than the pages that they may have been looking at that go with the example sketches.

From a code readability standpoint, I like:

pinMode(pin, INPUT, ENABLE_PULLUP);
pinMode(pin, INPUT, DISABLE_PULLUP);
pinMode(pin, INPUT);
pinMode(pin, OUTPUT);

When would this one be needed...

pinMode(pin, INPUT);

to stop old code from breaking, i think it would be harsh to make it a requirement to use the new syntax exclusively immediately and break old code.

also when you very first start and are introducing the idea of digital input and don't want to introduce to many things at once.

Its useful for people to build the circuit with the external resistor so they can see the resistor - it provides a concrete demonstration of the two resistances that gives a swing in voltage on the pin (one changing resistance, the switch being an extreme example of this, one fixed resistance - this is a really important concept for many sensors, and what follows soon after, analogue sensors) and to add the internal resistor into the mix while doing that complicates unnecessarily at that point. Then introduce the idea that you can reduce the number of external components for the digital pins, but the trade off is the code gets slightly more complicated.

Any of the suggestions in this thread would be an improvement but if the two parameter suggestion is semantically clearer then it deserves further consideration

when you very first start and are introducing the idea of digital input and don't want to introduce to many things at once.

Digital input is almost always introduced using buttons. These require pull-ups so the concept does need to be explained from the start, although often an external pull-up resistor is introduced before internal pull-ups. However I think that many non-technical people would find it easier to get something going if the button example used internal pull-ups instead of the external pull-downs:
pinMode(buttonPin, INPUT_PULLUP );
rather than having to understand and deal with : “a 10k resistor needs to be attached from pin 2 to ground” (sounds of user fumbling through pack of components trying to find the correct resistor)

Breaking existing code would be a problem but I wonder if there is any code that relied on the pull-ups being enabled when changing from output mode to input mode. Pull-ups change their state if the pin is switched to output mode and the pin state is changed, so code that does not explicitly set pull-up state when switching from output to input is a potential source of bugs. Is anyone aware of any code that does switch from output to input that requires pull-ups in a particular state but does not set them explicitly. If not, assuming the current method for setting pull-ups was still supported, old code would not break.

The example code that ships with arduino currently uses a switch connected to 5V, with a resistor to ground.

This was so you don't have to explain things like the switch pulled the digital in LOW, so digitalWrite HIGH to turn the LED on(it was counter-intuitive to a lot of people).

It feels to me like part of the dilema here is a choice of where you put that bit of complication as you start - a more complicated external circuit (an extra component and more wires), or more complicated code (if low write high, rather than if high write high, and possibly another parameter).

Having a simple code link between switch and LED in the button example, makes introducing pullups soon after more complicated, as you need to reverse the logic of the code/hardware as well at that point.

Having an external resistor at the beginning feels like more preperation for the road ahead to me (it will be needed for other circuits), but I know different people will have different takes on it.

Hi nick,

This was so you don't have to explain things like the switch pulled the digital in LOW, so digitalWrite HIGH to turn the LED on(it was counter-intuitive to a lot of people).

Yes, a lot of things that beginners need to know can be counter intuitive at first, but explaining it as follows is perhaps clearer:

“when the switch is pushed LOW (digitalRead is LOW) the LED is turned on (digitalWrite HIGH)”

As you say, different people have different takes on this, but FWIW, mine is that the following code (without the external resistor) is easer:

 void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);      
  // initialize the pushbutton pin as an input with pull-up resistor enabled:
  pinMode(buttonPin, INPUT_PULLUP);     
}

void loop(){
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pushed Down
  // if it is pushed, the buttonState will be LOW:
  if (buttonState == LOW) {     
    // turn LED on:    
    digitalWrite(ledPin, HIGH);  
  } 
  else {
    // turn LED off:
    digitalWrite(ledPin, LOW); 
  }
}

I don't think the your discription is significantly different :wink: the thing that confuses people is that one is off (the common perception of LOW), so why are you turning the LED on.

When the example in the distribution was as you code, but without enabling pullups you would still be using an external resistor to build the circuit as using the pullup is so opaque in code it was not appropriate for what maybe the second bit of code a beginner sees. so you had the comlexity of the code, and of the circuit.

If there was a more transparent syntax for pullups I think the balance would tip to what you suggested, a simpler build at first, against slightly more complicated syntax.

It kind of mirrors pin 13 with the flashing LED - you can just plug an LED in without worrying about resistors. Its easier at first as some physical complexity is hidden, but you then need to understand whats going on on the board to go further - if you want to go further you are more likely to have the motivation to do it.

It kind of mirrors pin 13 with the flashing LED - you can just plug an LED in without worrying about resistors.

Yes you can do that to make things simpler for beginners, but its not good practice. The current Arduino boards do not have a resistor in series with pin 13 (its only in series with the internal LED) so the typical red LED plugged in directly will exceed the Atmel recommended pin current.

That, and starting off with external pull-downs are the way many people have been introduced to Arduino, but they are not necessarily the best way to learn.

So far, this all this discussion about pinMode parameters, code readability and maintaining vs breaking backwards compatibility has missed what is by far the largest novice user problem with the pullup resistors.

The problem is inside digitalWrite(). It activates and deactivates the pullup resistor when the pin is in input mode.

Novice users (and even experts) sometimes forget to use pinMode() to put the pin in OUTPUT mode. This wouldn't be so bad, except that turning the pullup resistor on and off give the appearance the pin is working as an output.

Some time ago, at a local dorkbot meetup, a fairly new user was trying to build a simple tachometer for her bicycle, using a hall sensor hooked up to a pin and a bunch of LEDs to show if the code had detected changes. She was mostly using parts she'd found for free, and believed something was wrong with the LEDs, because they were working but only very dim. Several people looked at her board and tried different resistors, but nothing would work. I tried a 100 ohm resistor, no luck. Later, it occurred to me the pullup resistor was probably driving the LED, and indeed her code lacked pinMode()s to set the LED pins to output mode.

The primary problem is digitalWrite.

If the conceptual model being taught is the pin can be an INPUT or OUTPUT, when digitalWrite does something that appears to be working as an output, it is not following the user's understanding. The fundamental problem is the user's conceptual model of digitalWrite differs from its actual behavior.

Unfortunately, changing digitalWrite() to not touch pins when they're in input mode will break lots of code. The alternative is improving the user's model, but teaching input pullup resistors so early substantially raises the learning bar.

As another example, on a mail list a user was having a lot of trouble interfacing with a tiny motor. He had reason to believe 40 mA should be enough. Much discussion followed, about using transistors, about the actual needs of the motor, lots of back and forth about how to use a voltmeter, speculation if his multimeter's current mode was dead due to a blow fuse (all attempts read virtually no current). No matter what he tried, the pin would output only a fraction of a volt with the motor connected, but produced the full 5 volts without it. digitalWrite() struck again!

The huge problem is that unintentionally controlling the pullup resistor appears as if the pin is function as an output using digitalWrite(), leading the novice user and experts alike to pursue hardware troubleshooting, when in fact the problem is purely software.

Having said all that, I should mention that Teensyduino has disabled the pullup when pinMode(pin, INPUT) is used, and enabled it with pinMode(pin, INPUT_PULLUP). Over the last year, Teensy has been used with most Arduino libraries and lots of existing code, and this has caused zero problems (and believe me, I am well aware of pretty much every detail that has caused compatibility issues). Virtually all existing code that intentionally enables the pullup resistors does so by first called pinMode(pin, INPUT), and then the pullup is activated using digitalWrite(pin, HIGH). I am not aware of any existing code which first uses digitalWrite, even though that would also work, with the caveat of driving the pin as an output for a brief time.

However, to truly fix digitialWrite() so it fits the conceptual model taught, it really needs to not touch the pin when input mode is configured. That WILL BREAK ALL EXISTING CODE which uses pullup resistors.

Is the pain of reworking so much existing code worth it?

I'm not sure I see this as a big problem with digitalWrite(). "Your pin is working right as an output because you didn't set it as an output" is fairly understandable. Yes, it's weird that the pins sort of works even when set as an input, but I'm not sure that's a problem that requires changing the behavior of digitalWrite().