Cable tester, works with output high, need help understanding input low.


Wall of text incoming, any help much appreciated. You can probably skip down to the quoted post but I'm including it all to aid in understanding my situation.

I am building a device for quickly testing for correct connections on 3 pin XLR microphone cables. It indicates a green LED when the cable is good, a red LED when the cable has any problem, and a yellow LED which latches on a failed test in case the user misses a quick flash of the red LED on an intermittent cable. There is a momentary push button to clear the latched status. It is not necessary that the type of fault be indicated, GO/NO GO is enough as no failure will allow it to be used, and the repair tech will be able to see what the problem is when they open it.

I want to be able to test cables up to 100M in length, as might be necessary for testing channels in arena size multicore cables.

It is important to me that the code loop run no slower (be no longer) than is necessary, to minimise the time that an intermittent short can remain present without being detected and indicated.

I have assembled this on a breadboard with a clone Arduino Nano V3. It works, but I gather from reading the linked thread that this isn't the best option.

I'll explain here my rationale for my current design and copy in my code below.

I have configured three of the pins, the "source" end, as OUTPUT and the three pins on the "receive" end as INPUT. The procedure is to use digitalWrite to set a source pin high, and use digitalRead to see what is received where on the other end.

I have 470R resistors in series with the source output pins to protect them from each other if they are connected together in a shorted cable. Two of them in series between the high pin and the low pin giving 5/(2)470 = 0.005A, or 5mA. I suppose I would get 7mA if I had two low pins connected in parallel giving 470+(470/2).

As the receive end pins are high impedance of around 100MegOhm they are susceptible to floating voltages induced by nearby electromagnetic fields which could result in a receive pin being in a “high” state when it has not been connected to a voltage from a source pin. I have connected 5.1K pulldown resistors to these pins to mitigate this, but I am unsure how I should calculate the optimum value of these to produce an allowable voltage "float" for a given EMI situation.

The negative aspect of these resistors is that they form a voltage divider between the current limiting resistors on the source pins and the pull-down resistors on the receive pins. This reduces the 5V logic high, per Vin * (R2/(R1+R2)). This gives 5 * (5100/5100+470) = 5 * 0.916 = 4.579V. Because the MCU interprets any signal above 3V as “high”, this gives a margin of 1.5V.

There may be up to 10R of resistance per wire in a 100M cable.

Here is the code I am using: Note that I have been writing this in Sublime and copying it back to the Arduino IDE to compile and upload, so there may be a couple of syntax errors that aren't picked up until then. I may have made some minor changes since last compiling it, but nothing that changes the principle of operation. Some of the pin numbers have been chosen for reasons of physical assembly, putting all resistors on the same side of the Arduino to make the board narrower, for example.

int LatchClear = 3;
int ScreenReceive = 4;
int HotReceive = 5;
int ColdReceive = 6;
int ScreenSource = 7;                       
int HotSource = 8;
int ColdSource = 9;
int PassLED = 10;
int FailLED = 11;
int LatchFailLED = 12; 
int Score = 0;

void setup() {
  // put your setup code here, to run once:
pinMode(ScreenSource, OUTPUT);
pinMode(HotSource, OUTPUT);
pinMode(ColdSource, OUTPUT);
pinMode(ScreenReceive, INPUT);
pinMode(HotReceive, INPUT);
pinMode(ColdReceive, INPUT);
pinMode(PassLED, OUTPUT);
pinMode(FailLED, OUTPUT);
pinMode(LatchFailLED, OUTPUT);
pinMode(LatchClear, INPUT);

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

 if (digitalRead(LatchClear) == HIGH)
 digitalWrite(LatchFailLED, LOW); 
 } //Polls latch clear switch once per loop and turns off LatchFailLED if switch is pressed during poll. Switch may need to be held for duration of loop.
 (Score) = 0;                               //Resets score count to zero to begin new test.

 digitalWrite(ScreenSource, HIGH);          //Inject signal onto output XLR pin 1
 if (digitalRead(ScreenReceive) == HIGH && digitalRead(HotReceive) == LOW && digitalRead(ColdReceive) == LOW)    // Check if signal is received on XLR input pin 1 and pin 1 only.
  ++(Score);                                //Increment score count by one if above condition is true
 digitalWrite(ScreenSource, LOW);           //Remove signal from output XLR pin 1

  digitalWrite(HotSource, HIGH);          //Inject signal onto output XLR pin 2
 if (digitalRead(HotReceive) == HIGH && digitalRead(ScreenReceive) == LOW && digitalRead(ColdReceive) == LOW)    // Check if signal is received on XLR input pin 2 and pin 2 only.
  ++(Score);               //Increment score count by one if above condition is true
 digitalWrite(HotSource, LOW);           //Remove signal from output XLR pin 2

  digitalWrite(ColdSource, HIGH);          //Inject signal onto output XLR pin 3
 if (digitalRead(ColdReceive) == HIGH && digitalRead(ScreenReceive) == LOW && digitalRead(HotReceive) == LOW)    // Check if signal is received on XLR input pin 3 and pin 3 only.
  ++(Score);               //Increment score count by one if above condition is true
 digitalWrite(ColdSource, LOW);           //Remove signal from output XLR pin 3

 if ((Score) == 3)                       //Check if score is 3, which is only possible if all pins have passed.
 { digitalWrite(PassLED, HIGH);          //Turn on Pass LED if test has passed.  
 digitalWrite(FailLED, LOW); } //Turn off Fail LED if test has not failed, necessary to correct results of previous loop if conditions have changed.
 //Latching Fail LED not turned off here, because it is latching.

 else {digitalWrite(FailLED, HIGH); //Turn on Fail LED if test has failed.
 digitalWrite(LatchFailLED, HIGH);//Turn on Latching Fail LED.
 digitalWrite(PassLED, LOW); }      //Turn off Pass LED if test has failed, necessary to correct results of previous loop if conditions have changed.



I was sceptical about the method of reading the switch but I haven't had any issues. I suppose it doesn't particularly matter if it bounces a bit and resets the latch 5 times in a row. I came up with the idea of the incrementing "score" myself, I have no idea if that's how this sort of thing would normally be done, but it does seem to work. I should probably be using an interrupt for this to get it out of my main code loop, allowing it to loop faster, but I haven't sat down and understood them sufficiently yet.

The thread linked above indicates that I should have all pins configured as INPUT with pullup resistors enabled, and drive them low as a test stimulus.

Setting the pins to Inputs with internal pullup resistors provides a current limit situation for you.
Then driving 1 input low limits how much current will flow.
A good cable will have one input read as low, and 8 inputs read as high. If the intended input reads high and a Different input reads low, you know there is a miswire. If 2 inputs read back as low, something is miswired, or 2 wires are shorted.

What I don't understand is what "driving an input pin low" really means. I don't understand electronically what's happening.

Is is like in this image, but with the "switch" being toggled on and off internally by the MCU?

Is it as simple as removing the resistors from my circuit, changing all my test connections to INPUT_PULLUP and swapping "high" and "low" in my "if" statements? I have determined that the internal pullups on my Arduino are 36K, by setting an input high and sending it to ground via my multimeter. Why is such a large range of 20-50K given on the datasheet?

Thank you for any help with understanding this. I'm trying to lay it out in LTspice just to visualise it, but I thought it best to ask before I just confuse myself.


No, in that diagram the switch is an external button being pushed by the user. In your case it's the cable.

The 'resistors' inside the AVR chip are extremely poor tolerance. It turns out that it's actually difficult to make resistors directly on silicon. The chipmaking process is optimised to make tiny transistors and not much else. So the pullup resistor isn't actually a resistor - it's a MOSFET driven partly on. That also has its own inaccuracies so the effective resistance is very wide tolerance.

I would go with your original scheme as then you control the resistors directly. That other scheme is more like single-ended testing looking for dead shorts on the cable.

A simple solution: use analog inputs to determine the voltage at the "passive" end of the cable. Then you can add voltage dividers to the inputs, which provide a known voltage unless overridden by a signal from the "active" end of the cable (digital outputs). In perfect cable state the input voltages should follow the related output voltage (0/5V). A broken wire will show the value of the voltage divider (e.g. 2.5V), and shorts will show a dependency on other than the related output pin.

Current limiting resistors at the outputs will protect the outputs in case of shorts. These resistors become part of the voltage dividers, so that the measured voltage may not fully reach the 0/5V of the output pins. This deviation can be reduced by high resistor values for the voltage dividers, e.g. 2*100k for the divider, and 100 Ohm for current limiting.

Thank you. That's interesting to know about the internal pullups, was it a coincidence that mine was right on a standard resistor value?

I'm quite happy to keep the current scheme with the pulldown to suppress floating voltages, but the 5.1K is basically a guess, along with being a practical value for current draw. I don't know how much safety margin I'm giving myself. This will likely be inside a Hammond 1590B aluminium enclosure, but could have tens of metres of cable outside. There is a shield conductor, but that's one of the ones I'm testing...

Would the voltage divider be functioning as a "divided pullup" with the test signal be connected to the centre point of this? If I chose the values of the divider to put the divided voltage below the 3V needed for "high" I could probably do it with a digital pin and slightly simpler code. What I don't understand is why this is better than just using a pulldown without the extra pullup to create the divider. Will a divided voltage be more stable at say 2V than one that is simply pulled down to 0V? Ultimately, the goal is to maintain separation from 3V when no test signal is connected.

I need to carefully think through the various combinations of resistors that would be created for each fault condition and calculate what would end up at which pin. At least I only have to ensure that the cable will fail at least one of the nine tests when it has any fault, so there's quite a bit of tolerance for missing a problem in one spot provided it causes one elsewhere.


Okay, a digital input has only two states. With the pullup resistors enabled at the inputs, the default state (broken or not driven or driven high) is high. If then a single line is driven low, the corresponding input should read low, else the line is broken. Also no other line should become low, else a short is detected. This test has to be repeated with every line, with all other outputs disabled or high.