Half duplex: How to prevent shorting?

Consider two Arduinos (ATmega328P), with pins 2 connected for half-duplex communication (e.g. using SoftwareSerialWithHalfDuplex):

Both Arduinos may be transmitting at the same time, either for logic reasons, or if something is wrong. In this case, one Arduino may set pin 2 to high, the other Arduino may set it to low. The result is a short circuit. To limit the current to 5mA, I could add a 1kΩ resistor:

Is that the preferred solution? Am I missing something?

• Read pipe's answer carefully. The diagram in his answer is not complete. Because you don't know the solution I suspect you don't know what a pull-up resistor is. Google "pull-up resistor". And yes, pipe is correct: do not communicate by setting pin high and low. Communicate by hardcoding the pin to low and setting it as input and output. When it's in output mode it will try to drain all voltage on the line giving you 0. In input mode it will not produce any voltage so the pull-up resistor is free to supply voltage giving you 1. If both transmit 0 = no issue. Both transmit 1 also no issue Apr 12 '18 at 18:26
• @slebetman Please see the help section on comments to learn how comments on stack exchange are used. If there are problems with my answer the comment belongs there. If you want to answer the question, you should post it as a real answer that can be voted on. Answers in comments goes against the Stack Exchange model of community vetted answers.
– pipe
Apr 12 '18 at 20:59

That is a reasonable way to solve the problem with accidental collisions.

Another common method is to use an open drain output. It is the method used by I2C to ensure that two colliding drivers will not draw too much current, and to ensure that the signal will either be low or high, and not something in between.

This way you can connect as many outputs as you wish, because they either do nothing, or pull the output to ground. If two or more pins drive the same signal to ground, nothing bad happens.

To make this work, you need a single pull-up resistor on the bus, and since no one is actively driving the signal high, this makes for a lower maximum bit rate.

Since you are using a software-driven serial interface, it should be easy to adapt to your needs. Instead of setting up the ports as push-pull outputs and then setting them as either low or high, you set them up as inputs with pull-up when they are supposed to be output high or input, and set them up as output low when they are supposed to be output low.

• You can mimic open drain using Arduino functions. In the setup(), drive the pin low with digitalWrite(pin,LOW). When doing communications, toggle between input and output using pinMode(pin,INPUT) for high, and pinMode(pin,OUTPUT) for low. Or use the IO registers directly, setting the corresponding PORT bit low and toggling the corresponding DDR bit. Apr 12 '18 at 10:09
• You can even use pinMode(pin, INPUT_PULLUP) for sending 'high' making use of the internal pull-up resistors of the AVR. Apr 12 '18 at 12:24
• @JimmyB: That doesn’t protect against programming errors though. Apr 12 '18 at 15:57
• @feklee I2C can detect bus collisions by reading the value of the pin each time it writes a bit. If it writes a one but reads a zero, it knows somebody else is using the line, so gives up. Apr 12 '18 at 20:26
• @JimmyB you can, but you have to be careful. If using the pull-up resistor, you have to remember to turn the pull-up off before changing to an output. Otherwise you drive the line high. Apr 12 '18 at 20:28

It's as good as anything else. An alternative would be to operate the pins in open-drain mode, but then you'd need an external pullup resistor.