[SOLVED] same digital pin as input and output

Hello everybody !

New forum member here, altough I've read and learned a lot for a year from this forum :slight_smile:
My background: 20+ years of software development, but a noob in electronics.

Anyway, here's my problem...


My goal:

I want to use the same digital pin on an arduino (mega) as an input with a switch and as an output with an LED, both "at the same time".
By "at the same time", I mean that the pin would be configured as an output to light (or not) an LED to show a status, then configured as an input to check if a momentary switch is pressed, then configured back as an output, and so on.


Why am I doing this ?

Well I've got some normally-open momentary switches already connected and working (home automation), but I would like to add some status LEDs near the switches (but not logically related to the switches), without adding wires (long distance, and it was a pain to pull all those wires through the walls and ceilings in my home, I don't want to add a bunch of additional wires).


What I've got so far:

I rigged everything up on a breadbord.

shematic:

GND -----------[ resistor ]-----+-------[ LED ] --------+----- [ switch ]--------- io Pin
                                +                       +
                                +-----[ capacitor ]-----+

The capacitor is here to avoid the LED to be dimly lit while the pin is configured as an input.

code:

Most of the time the pin is an output pin, and 10 times a second we use it as an input pin to check the switch.
No debounce is handled in this test program.

#define ioPin 12
short myStatus;
unsigned long lastReading;

void setup (void)
{
	pinMode (ioPin, OUTPUT);
	myStatus = HIGH; // note: as this is a test program, status would be updated by some other logic inside loop(). here it is set to HIGH all the time.
	lastReading = 0;
	Serial.begin (9600);
}

void loop (void)
{
	short	reading;


	// update LED
	digitalWrite (ioPin, myStatus);

	// 100 ms have passed ?
	if (millis () - lastReading > 100)
	{	
		lastReading = millis ();
                
                // read switch
		pinMode (ioPin, INPUT);
		digitalWrite (ioPin, HIGH); // use pullup resistor
		reading = digitalRead (ioPin);
		pinMode (ioPin, OUTPUT);
		
		Serial.println (reading, DEC);		
	}
}

What I need:

I can read the switch fine. That part seems to be ok.

But the status is reflected on the LED only when the button is pressed obviously, since I'm using a normally-open momentary switch.

I need a way to let the current go trough the circuit so the status is always shown on the LED when nobody uses the switch. So I could use a normally-closed momentary switch, but unfortunately these are not available by the maker of my switches (Legrand in France)… mine are regular (altough temporary) 220v wall mounting switches, but connected on the arduino instead of the mains.

I've got space left on the arduino end to add electronics, and some space on the switch + LED end, but can not add additional wires between the arduino and the other end.

I hope all this makes sense ? Fell free to ask for more details :slight_smile:

EDIT:
corrected schematic, thanks Mike for pointing that out !

It looks from that schematic that when the pin is used as an input and your switch is not closed then the input is floating. This is not a good idea because the input state is not defined. This will result in unreliable operation.

I feel that some of your reasoning especially about the role of the capacitor is quite frankly rather bazaar and quite wrong.

Hi Mike, and thanks for looking

Oups, you're right, my schematic is wrong: VCC should in fact be GND…
And as I use the internal pullup resistor, the input shouldn't be floating I guess.

I did a first try on the breadboard without a capacitor, but the LED was dimmed when the pin was set as an input (as descriped on http://arduino.cc/en/Tutorial/DigitalPins ). It was kind of blinking at 10 Hz since I check the input pin 10 times a second. So adding a capacitor smoothed that out. I tried different values, but I'm at work right now and didn't note the value before leaving this morning.

So as you have corrected it now I can't see how the LED can light up at all if the switch is open. There is no source of current.

Is that what you have got?

The LED was lighting up dimly before because it was getting current through the internal pull up, but a capacitor is not the answer.

Yes, this is exactly what I get: the LED lights only when the button is pressed.

The capacitor is just an answer to the dimly blinking, not to the main problem.

Basically I'm looking for a way to transform a normally-open momentary switch into a normally-closed momentary switch, using some electronics, and without replacing the existing normally-open momentary switch.

GND ----------[ resistor ]-----+-------[ LED ] --------+-------------- io Pin

+-----[ switch ]-------+

OR

GND -------+----[ resistor ]------------[ LED ] --------+-------------- io Pin

+-----[ resistor ]---------[ switch ]-------+

This may work:
Drop off voltage on led create a difference in switch button readings, green and blue leds 3.V more than enough to read it as "HIGH", red leds probably needs two in series.

Thanks Magician,

Going to try that once I'm back home !

You will find with that arrangement, when the LED is on a pushing the switch will turn it off and when the switch is being read the LED will flash on unless the switch is pressed.

Here's another way:

  • connect LED and series resistor between pin and Vcc
  • connect switch between pin and ground

To read switch, set pin mode to input, enable internal pullup, and read from pin. Pressing the button will have the side effect of turning the LED on while the button is pressed.

  • connect switch between pin and ground

And what happened if you press a switch when pin is output and "HIGH"?

when the LED is on a pushing the switch will turn it off

Not with second drawings, resistor in series with switch ~1 kOhm area, almost no effect on LED.

and when the switch is being read the LED will flash on unless the switch is pressed.

Well, "dimly " led will persist, the same time it would be greatly reduced by PWM where duty cycle is defined by input/output timing. Taking reading as fast as possible, in microseconds, while overall freq 1/10 sec. drive pwm to 10^-5. I think, it wouldn't be noticeable, .

Magician:

  • connect switch between pin and ground

And what happened if you press a switch when pin is output and "HIGH"?

Good point. To use this arrangement you need to drive the pin as follows:

  1. To turn the LED off, set pin mode to INPUT
  2. To turn the LED on, first digitalWrite LOW to the pin, then set pin mode to OUTPUT
  3. To poll the switch, first set pin mode to INPUT, then digitalWrite HIGH to the pin. After reading the switch, do (1) or (2) to restore the desired LED state.

The key is never to have the pin configured as an output and high at the same time.

O'k, I may work, only problem 3 wires: Vcc, Gnd, Pin.....

I got it working with the help of Magician's 1st schematic.

I replaced the resistor with a 10k potentiometer, and put my scope's probe on the digital ioPin.

Depending on the position of the wiper, the voltage at the pin goes from 1.8 v to 2.4 v with the switch not pressed, thus reading LOW below around 2.2 v and HIGH above.

With the switch pressed, the voltage varies from 0 to 1.1 v, which always gives a LOW reading.

So calculating the right resistance to have more than 2.2 v at the pin will do the job.

The only drawback from this method is that due to the relative high resistance value, the LED is not very bright.

Then I thought of using an analog input pin, so I could decide myself which value defines the limit between "LOW" and "HIGH". This allows me to keep the resistance value low to have a bright LED: I choose 470 ohms for my yellow LED, which gives a reading of around 400 when the switch is not pressed and 40 when pressed.

The LED is always off when the button is pressed, but that is ok for me.

Thanks again everybody ! :slight_smile: :slight_smile: :slight_smile:

#define ioPinA 0
#define ioPinD A0

short myStatus;
unsigned long lastReading;
unsigned long lastSwap;

void setup (void)
{
	pinMode (ioPinD, OUTPUT);
	myStatus = HIGH;
	lastReading = 0;
	lastSwap = 0;
	Serial.begin (9600);
}

void loop (void)
{
	short	reading;
        
        // blinking led
	if (millis () - lastSwap > 500)
        {
		lastSwap = millis ();
                myStatus = HIGH - myStatus;
        	digitalWrite (ioPinD, myStatus);
        }

	// 100 ms have passed ?
	if (millis () - lastReading > 100)
	{	
		lastReading = millis ();
                
                // set as input
	        digitalWrite (ioPinD, LOW);
		pinMode (ioPinD, INPUT);

                // read switch
		reading = analogRead (ioPinA);

                // set as output and restore LED status
		digitalWrite (ioPinD, LOW);
		pinMode (ioPinD, OUTPUT);
		digitalWrite (ioPinD, myStatus);

		Serial.println (reading, DEC);	
		reading = reading > 200 ? HIGH : LOW;
		Serial.println (reading == HIGH ? "HIGH" : "LOW");	
	}
}

ps: sorry dc42, only two wires available…

edit: typos…

1 Like