Hack open the cheapest $20 wire-tangled KVM available and hide it under your desk using an Arduino UNO to build your own urgently needed remote-control.
Introduction
Turn a tangled wire-mess of an accident waiting to happen on your desk into a sleek and convenient remote-controlled majesty.
KVM-Switch stands for:
and allows a computer user to control multiple computers with a single Keyboard-Video-Mouse. They're probably a lot cheaper than they were, as most things are, but to be honest, I never heard of them until I had two keyboards on my desk next to a pair of mouses and decided I needed to do something about them. So I went on eBay and found the cheapest one, figuring that would be just fine. Then, even before I ever got it in the mail, I started wondering how I was going to connect this thing because it has no remote control and I like to hide all those nasty wiry things away from my work area, and the one I ordered didn't have a remote-control. There are some available with remote controls but they're considerably more expensive.
As it was, I had no intention of using that el cheap'o KVM without a remote and sat on it with my multiple keyboards and mices, mouses and meese until I went shopping again and saw that the other better ones were nearly $200.00 ... and I am way too cheap for that. Especially if buying a better one denies me the opportunity to make my own, which is priceless, really.
Though the financial burden be small, the excited boyish glow glimmering past the gray bristles crowding around the crow's feet of my eyes in the morning .... priceless.
Cost:
- Arduino UNO $10
- buttons, wires, LEDs ... pennies
- OLED screen $5
- USB adaptor $4
- KVM-switch on eBay $12
Total cost : $31.00
which is not nothing but is a lot cheaper than the $200.00 I had in my virtual 'shopping cart' before deciding to perform surgery on the crappy one I wasn't using anyway.
Watch a short video about this project.
Background
The KVM I have has 4 input ports (can control up to 4 computers) and 3 USB 2.0 for user-interface (mouse, keyboard, USB-drive).
Since I had no intention of cluttering my work area with a jumble of wires, a box and the dreary sadness of it all, I yanked out my trusty Philipshead screwdriver and wound and wound and wound the screws until the lid fell off and all was revealed. It has four indicator LEDs to tell the user which computer is currently selected and one push-button. The solder joints for all of these are immediately evident the moment you open it up, it's like it was made to be hacked. No need to worry about offending anyone by making the following changes since you'll need to fork out $12.49 to buy one from China and they'll be happy to take your money and help you put that Arduino twinkle in your eye. Turns out, 4 LEDs and a single push-button are extremely simple to hack. I tested the voltage across the LEDs, discovered they were minimal (about half a Volt) and grounded the button's far-line to test my theory and ... Lo and behold, the channel was changing at my magical touch. It really was quite a marvel.
A bit of soldering, another cup of coffee and I soon had the lid of it screwed back on and started working on the Arduino end with its buttons of mystery and LEDs of scintillating effluence.
I didn't initially plan on writing an Arduino program for it. As the voltage across the resistors was minimal, I tapped into their anodes and drew the wire to my remote. Then I ran the LED voltage lines through the Base of four transistors (one for each computer port) and used a remote push-button wired in parallel to the one on the KVM. The transistors being active when the KVM's indicator LED was lit let current flow through its Emitter which lit an LED on my remote.
I was using one of the four computer input USBs as a dedicated VCC to power the LEDs and all was right with the world...
Except it wasn't. Because... you know. It just isn't.
So... I decided to go all-out Arduino on it and pulled an old UNO that was once a robot-arm-controller, a burpees reps-counter and a test-pilot for my many desktop adventures. Armed with this piece of electronic joy, I did things a little differently.
And this is how it all happened ...
KVM Remote Controller
Looking at the image below, you will see that all of the Arduino UNO's analog lines are in use. That's because I connected the OLED's I2C wires to the UNO's A4(SDA) & A5(SCL) lines, the voltage-divider button controls are tied to analog pin A0 and the remaining A1, A2 and A3 pins are used to determine which of the KVM's four indicator lights is lit.
"But wait!" you may ask... fair question and no, I don't eat bologno on Tuesdays. Its just a rule.
But besides that, you may wonder how it's testing the voltage of 4 indicator lights with only 3 analog pins. And that's a fair question too... the answer is : It doesn't.... sorta.
The KVM only has four possible PC ports to choose from and since one of those four ports is selected at all times, the Arduino only needs to test three of them and decide, as Sherlock Holmes is fond of doing,
When you have eliminated all which is impossible, then whatever remains, however improbable, must be the truth.”
~ Arthur Conan Doyle, The Case-Book of Sherlock Holmes
So if you know it ain't one of the three lines you've tested ... its likely that the fourth one is selected.
Using all of the Arduino's analog pins is not necessary. The UNO has pins D18/D19 that can be used for the I2C interface instead of A4 & A5 analog pins. The 4 button controller here uses up one analog pin as well but you could get rid of the voltage divider and put them on 4 digital pins or even use a load-register with 3 digital pins(to save only 1 analog pin seems a little desperate). Alternately, you could wire the KVM's LEDs to base pins of 4 transistors to control their output voltages that generate voltage high enough for the microcontroller to read them as logical 1 (HIGH or true) then run the four amplified KVM LED signals into 4 pins of a load register and use the same load register's remaining 4 pins to control the 4 buttons.
Whatever lights your world...
N.B. - all of the KVM's USB inputs (both mouse/keyboard peripherals & PCs) need to have a common ground. You'll need to solder the ground pins of all your USB terminals together and tie them to the KVM button's ground pin or risk having pretty lights but now mouse or keyboard.
Button Switch
By tying the far-end of the button to one of my digital lines, the KVM can be made to think that its button has been pressed when the digital line it is connected to is set to LOW for 20ms and set HIGH again. As the button itself closes a circuit to ground, the toggling of the digital pin's HIGH/LOW/HIGH value electrically simulates the closed-circuit of an activated push-button so the KVM reacts by cycling through one computer port.
My original remote control still only had one button and four lights, not quite the best but at least the KVM was hidden away somewhere even if I still had to press the remote button several times to cycle around to the computer I wanted.
But with the Arduino in play, it was so easy to tell the microcontroller to run four lights and buttons. It was really quite simple, the code itself is extremely easy to write and understand. There are only four buttons and matching LEDs, along with an OLED screen.
Arduino makes dull things shine ... I'd make a shiny Arduino cat-litter box but I don't have a cat.
It keeps track of the latest button pressed, which it calls 'the long sought PC_Requested'. As the three analog lines test the KVM's indicator light voltages, these analog lines provide the app with the state of the KVM's current selection. This 'current-selection' value mirror the LEDs voltages on the KVM, in that, the first LED to record a voltage above a base detection voltage level will be assumed to match the current selection as it tests through a series of if
-else
-if
statements to check.
The CurrentPC()
method is used to determine which state the computer is currently in.
Have a look at the code:
int CurrentPC()
{
int intMinDetection = 250;
int intPC1 = analogRead(pinPC1);
int intPC2 = analogRead(pinPC2);
int intPC3 = analogRead(pinPC3);
if (intPC1 > intMinDetection)
return 1;
if (intPC2 > intMinDetection)
return 2;
if (intPC3 > intMinDetection)
return 3;
return 4;
}
The loop()
method tests for a button-press and sets 'intPC_Requested
' if a button has been pressed. If this value does not match the 'intPC_Current
' returned by the above mentioned method CurrentPC()
, then it will simulate a button press and cycle to the next PC port on the KVM until the intPC_Requested
is equal to the intPC_Current
.
void loop()
{
delay(50);
int intBtnPressed = ButtonPressed();
if (intBtnPressed != intBtn_Last)
{
intBtn_Last = intBtnPressed;
bool bolIgnoreLEDPower = false;
if (intBtn_Last != 0)
{
intPC_Requested = intBtn_Last;
if (!bolLED_Power)
bolLED_Power = true;
else if (intBtnPressed == intPC_Current)
bolLED_Power = false;
setLEDs();
OLED_Display();
bolIgnoreLEDPower = true;
}
}
intPC_Current = CurrentPC();
if (intPC_Current != intPC_Requested)
{
CyclePC();
intPC_Current = CurrentPC();
}
else
{
setLEDs();
OLED_Display();
}
}
4 User-Buttons
The four push-buttons connected at the user-end of things are each tied to their own resistor values that then make four voltage dividers with one common soldering joint which is tied to one of the Arduino's analog pins. Since each of the four resistors is unique, each of the four voltage dividers produce four different levels of voltage whenever their respective buttons are pressed. As the Arduino is capable of measuring a voltage between 0-5Volts to a precision of about 5mV figuring out which button was pressed is a matter of testing that pin's input voltage and going through nested if
-else
statements until you have a match. Debouncing is an issue which arises with all mechanical connections like push-buttons in electronics. The button makes and breaks contact several times in the initial micro-seconds the button is pressed. This is not always a problem but since the Arduino doesn't take the time to test things all that thoroughly when it passes by a line of code like:
int intAnalogPin_Voltage = analogRead(pinButtons);
The microcontroller takes one quick sample and runs off with the news. Which is great, that's what you want. But it may take that 'one quick sample' during some phase of your buttons bounciness and give you a false report.
Also, voltage values don't just jump from 0 to 5V instantly. There is a transition. If the Arduino tests the voltage level while the tide of current is still just rising you won't get an accurate reading then neither.
So... debouncing is an issue.
To handle this, this project takes three samples at 20ms intervals. If they all match the same value, it reports a button press otherwise ... it didn't see nothing.
Move along... there ain't nothing here.
Have a look at the code below:
int _ButtonPressed()
{
int intButtons = analogRead(pinButtons);
if (intButtons > 920) return 0;
if (intButtons > 750 && intButtons < 900)
return 1;
if (intButtons > 545 && intButtons < 720)
return 2;
if (intButtons > 375 && intButtons < 515)
return 3;
if (intButtons > 85 && intButtons < 222)
return 4;
return 0;
}
int ButtonPressed()
{
int intDelay = 20;
int intBtn_A = _ButtonPressed();
delay (intDelay);
int intBtn_B = _ButtonPressed();
if (intBtn_A == intBtn_B)
{
delay( intDelay);
int intBtn_C = _ButtonPressed();
if (intBtn_A == intBtn_C)
return intBtn_A;
}
return 0;
}
If you want to learn more about the voltage-divider button controller, have a look at my article Exercise Reps Counter.
The interface itself, is as straight forward as four LEDs and one button can get but if you want to toggle off the light (only because I neglected to cut the power to it with an on/off toggle switch), you can shut off the LEDs and leave the OLED black by pressing the button next to the LED that is currently lit. And pressing any button will turn them on again.
OLED Headings
Since the KVM is part of my computer system, it is never far from the Arduino IDE and a USB connection. For this reason then, I avoided making a complicated interface for the user to 'type' text into the project telling it what to write for any given port 'LapTop', "DeskTop" & "Unassigned" are hard-wired into the code and can be easily edited as needed with a single USB connection.
void OLED_Display()
{
oled.clearDisplay(); oled.setTextSize(2); oled.setTextColor(bolLED_Power ? WHITE : BLACK); oled.setCursor(0, 10);
if (bolLED_Power)
{
switch (intPC_Current)
{
case 1:
oled.println(F("Unassigned"));
break;
case 2:
oled.println(F("DeskTop"));
break;
case 3:
oled.println(F("Unassigned"));
break;
case 4:
oled.println(F("Laptop"));
break;
}
}
oled.display(); }
and that's about it!
History
- 5th February, 2022 - First published
- 20th February, 2022 - added note about grounding all USB terminals