8 Port IP KVM on a budget

Posted by Colin on Fri, Jan 31, 2025

We recently had a small server outage in work. Nothing mission critical, it was over a weekend and coming up to Christmas holidays. It could wait, but that little itch in my brain couldn’t let it sit. I tried several remote remedies to bring it back online but it needed physical access. It had to wait until Monday.

We use older Mac Minis as compact Linux servers and they have been working brilliantly for years. And so much quieter than sitting beside a massive rack of noisy servers!

But if they go down physical access can be a pain as we don’t have a KVM switch. I really wanted to go for an IP KVM switch but they are so expensive! So I figured we could come up with a cheaper solution using, you guessed it, a Raspberry Pi!

So the plan was to get a cheap 8-port USB and HDMI KVM switch, attach it to a PiKVM device to get it on the network and Bob’s your uncle. I hit a few snags along the way but we got it running, so this is documentation of the process for my own records and to help anyone else hoping to replicate this.

Parts

8 port KVM Switch

Step 1: Fit the KVM Switch

This part was the easiest. Just hook up all your servers to the KVM switch and test it out.

However it was at this point I noticed what could have been a show stopper. Most KVM switches have a key sequence you can use to switch between machines, usually something like tapping Scroll Lock twice and then hitting Escape, or Scroll Lock followed by the number of the port you wish to switch to. This one didn’t! It comes with a switch panel with 8 buttons on it and an IR remote, as well as being able to press the illuminated buttons on the front panel.

Step 2: Setup and connect PiKVM

This step is well documented at the PiKVM website. Basically download the appropriate image for your Raspberry Pi it will be hosted on (I used a Pi4), flash it to an SD card, boot the Pi and then SSH in to configure some options.

You will also need to connect the WaveShare HDMI capture device to the CSI port on the Pi and connect the HDMI output from the KVM switch to the Pi.

BONUS TIP: If you connect this via the HDMI mirror splitter device, you can have the display going to the PiKVM AND a physical monitor, which means we can remote in or just use the physical keyboard etc at the server rack.

Then you will need to connect the power to the PiKVM via the USB-C Power and data splitter cable, so that power goes into the Pi 4 power port and then use a USB-C to A cable to connect the other half to the KVM switch keyboard and mouse port.

This KVM switch has 4 ports so we are able to connect the PiKVM for control as well as a physical keyboard and mouse.

Step 3: Reverse engineer the control box!

As previously mentioned, the KVM switch did not have keyboard hotkey switching so I couldn’t remotely switch from the PiKVM software. Oops!

So I took apart the control box that was connected via a USB cable. Unfortunately it doesn’t use the USB protocol, just the cables! Luckily a bit of Googling located someone who had already worked out the communication signals it was using. It seems to send a pulse of varying length to choose the port it switches to.

I used the Python code from here and saved it to /usr/local/bin/kvm.py making sure to chmod +x to make it executable.

This reddit post also has some useful pointers.

/usr/bin/local/kvm.py

 1#!/usr/bin/env python
 2import RPi.GPIO as GPIO
 3import sys
 4import time
 5
 6input_pin = 24  # connected to 'D+' pin on mini USB
 7output_pin = 23  # connected to 'D-' pin on mini USB
 8
 9
10def setup():
11GPIO.setmode(GPIO.BCM)
12GPIO.setwarnings(False)
13GPIO.setup(input_pin, GPIO.IN)
14GPIO.setup(output_pin, GPIO.OUT)
15GPIO.output(output_pin, GPIO.HIGH)
16
17
18def get_port() -> int:
19while GPIO.input(input_pin) == GPIO.LOW:
20time.sleep(0.001)
21while GPIO.input(input_pin) == GPIO.HIGH:
22time.sleep(0.001)
23start = time.time()
24while GPIO.input(input_pin) == GPIO.LOW:
25time.sleep(0.001)
26stop = time.time()
27return round((stop - start) * 50)
28
29
30def set_port(port: int):
31GPIO.output(output_pin, GPIO.LOW)
32time.sleep(0.02)
33GPIO.output(output_pin, GPIO.HIGH)
34time.sleep(0.02)
35GPIO.output(output_pin, GPIO.LOW)
36time.sleep(0.02 * port)
37GPIO.output(output_pin, GPIO.HIGH)
38
39
40setup()
41set_port(int(sys.argv[1]))

Then in the /etc/kvmd/override.yaml file I added the following:

Override.yaml

 1kvmd:
 2  gpio:
 3    drivers:
 4      kvm_set_input_1:
 5        type: cmd
 6        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 1]
 7      kvm_set_input_2:
 8        type: cmd
 9        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 2]
10      kvm_set_input_3:
11        type: cmd
12        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 3]
13      kvm_set_input_4:
14        type: cmd
15        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 4]
16      kvm_set_input_5:
17        type: cmd
18        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 5]
19      kvm_set_input_6:
20        type: cmd
21        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 6]
22      kvm_set_input_7:
23        type: cmd
24        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 7]
25      kvm_set_input_8:
26        type: cmd
27        cmd: [/usr/bin/sudo, /usr/local/bin/kvm.py, 8]
28    scheme:
29      kvm_input_1_button:
30        driver: kvm_set_input_1
31        pin: 0
32        mode: output
33        switch: false
34      kvm_input_2_button:
35        driver: kvm_set_input_2
36        pin: 0
37        mode: output
38        switch: false
39      kvm_input_3_button:
40        driver: kvm_set_input_3
41        pin: 0
42        mode: output
43        switch: false
44      kvm_input_4_button:
45        driver: kvm_set_input_4
46        pin: 0
47        mode: output
48        switch: false
49      kvm_input_5_button:
50        driver: kvm_set_input_5
51        pin: 0
52        mode: output
53        switch: false
54      kvm_input_6_button:
55        driver: kvm_set_input_6
56        pin: 0
57        mode: output
58        switch: false
59      kvm_input_7_button:
60        driver: kvm_set_input_7
61        pin: 0
62        mode: output
63        switch: false
64      kvm_input_8_button:
65        driver: kvm_set_input_8
66        pin: 0
67        mode: output
68        switch: false
69
70  view:
71    header:
72      title: Input
73    table:
74      - ["#Console:", "kvm_input_1_button|Prox1", "kvm_input_2_button|Prox2", "kvm_input_3_button|Prox3", "kvm_input_4_button|Lambda", "kvm_input_5_button|NA01", "kvm_input_6_button|NA02", "kvm_input_7_button|ProxBackup", "kvm_input_8_button|FOG"]

The bottom section labelled “view” creates a new button in the top menu of PiKVM called Input, and then it has 8 buttons within it. The names of the buttons are Prox1, Prox2 etc. You should change those to the names of the machines you have connected to each port.

Then I cut open an old Mini-USB cable and exposed the data lines. You do not need the red or black power lines. I soldered on DuPont connectors and attached the data lines to pins 23 and 24 on the Pi GPIO pins.

Mini-USB connections GPIO

I then tested the switching using the kvm.py script above from the command line. Bingo!

Conclusion

It was a lot of fiddling about but we now have web based KVM access to all of our servers and the ability to easily switch between them in the web interface.

PiKVM also has the ability to emulate a USB disk or CD drive, so you can upload ISOs to it and attach them, allowing you to fully reimage a machine remotely. It really is a fantastic project and I am glad it exists!



comments powered by Disqus