Mapping a USB volume knob into a keyboard on Linux for SDR

December 16, 2019

I recently discovered the existence of USB volume knobs. A Reddit user posted an article about reflashing the firmware on one to convert it to a keyboard.

 

IMG_20191216_010138

Inspired, I picked one up for $18 on eBay (“USB Volume Controller Knob Adjuster Switcher for Tablet PC Speaker Audio“) and thought that I might be able to do something similar.

It turns out, under Linux, this is pretty easy.

First, I plugged in the volume knob and saw that Linux detected it correctly and used it to adjust the volume. That was a promising start. I could see it show the “HDMI / DisplayPort” volume – and it went up when I turned the knob to the right, down when I turned the knob to the left, and muted when I pressed the knob.

Next, I wanted to see what events were being generated. I found some very useful instructions at https://yulistic.gitlab.io/2017/12/linux-keymapping-with-udev-hwdb/ and did them:

$ cat /proc/bus/input/devices
...
I: Bus=0003 Vendor=0483 Product=572d Version=0111
N: Name="STMicroelectronics USB Volume Control"
P: Phys=usb-0000:00:1d.0-1.7.2.4.3.1/input0
S: Sysfs=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.7/2-1.7.2/2-1.7.2.4/2-1.7.2.4.3/2-1.7.2.4.3.1/2-1.7.2.4.3.1:1.0/0003:0483:572D.0008/input/input14
U: Uniq=2070363C4250
H: Handlers=kbd event8 
B: PROP=0
B: EV=13
B: KEY=3800000000 e000000000000 0
B: MSC=10
...

This showed me a few useful things:

  • The device vendor for my device is 0483 (the “I:” line)
  • The product ID for my device is 572d (also on the “I:” line)
  • The device is attached on /dev/input/event8 (on the “H:” line)

So now I could scan the events that came across when I moved the knob:

$ sudo evtest /dev/input/event8
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x483 product 0x572d version 0x111
Input device name: "STMicroelectronics USB Volume Control"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 113 (KEY_MUTE)
    Event code 114 (KEY_VOLUMEDOWN)
    Event code 115 (KEY_VOLUMEUP)
    Event code 163 (KEY_NEXTSONG)
    Event code 164 (KEY_PLAYPAUSE)
    Event code 165 (KEY_PREVIOUSSONG)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)
Event: time 1576479720.245227, type 4 (EV_MSC), code 4 (MSC_SCAN), value c00e9
Event: time 1576479720.245227, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 1
Event: time 1576479720.245227, -------------- SYN_REPORT ------------
Event: time 1576479720.253248, type 4 (EV_MSC), code 4 (MSC_SCAN), value c00e9
Event: time 1576479720.253248, type 1 (EV_KEY), code 115 (KEY_VOLUMEUP), value 0
Event: time 1576479720.253248, -------------- SYN_REPORT ------------
Event: time 1576479722.325231, type 4 (EV_MSC), code 4 (MSC_SCAN), value c00ea
Event: time 1576479722.325231, type 1 (EV_KEY), code 114 (KEY_VOLUMEDOWN), value 1
Event: time 1576479722.325231, -------------- SYN_REPORT ------------
Event: time 1576479722.333224, type 4 (EV_MSC), code 4 (MSC_SCAN), value c00ea
Event: time 1576479722.333224, type 1 (EV_KEY), code 114 (KEY_VOLUMEDOWN), value 0
Event: time 1576479722.333224, -------------- SYN_REPORT ------------
Event: time 1576479724.381251, type 4 (EV_MSC), code 4 (MSC_SCAN), value c00e2
Event: time 1576479724.381251, type 1 (EV_KEY), code 113 (KEY_MUTE), value 1
Event: time 1576479724.381251, -------------- SYN_REPORT ------------
Event: time 1576479724.389251, type 4 (EV_MSC), code 4 (MSC_SCAN), value c00e2
Event: time 1576479724.389251, type 1 (EV_KEY), code 113 (KEY_MUTE), value 0
Event: time 1576479724.389251, -------------- SYN_REPORT ------------

Neat, even more useful things. In particular:

  • When I turn the knob to the right, I get an MSC_SCAN event of type c00e9 (along with a KEY_VOLUMEUP event)
  • When I turn the knob to the left, I get an MSC_SCAN event of type c00ea (along with a KEY_VOLUMEDOWN event)
  • When I push on the knob, I get an MSC_SCAN event of type c00e2 (along with a KEY_MUTE event)
  • Apparently the firmware supports KEY_NEXTSONG, KEY_PREVIOUSSONG and KEY_PLAYPAUSE as well. Huh.

I want to map those MSC_SCAN events to different key codes. In particular, I want a cursor-left key when I turn the knob to the left, a cursor-right key when I turn the knob to the right, and something useful (say, pressing the “m” key) when I press the knob. So I created a hwdb file for my device:

$ cat /etc/udev/hwdb.d/99-usb-knob.hwdb
evdev:input:b*v0483p572D*
 KEYBOARD_KEY_c00ea=left
 KEYBOARD_KEY_c00e9=right
 KEYBOARD_KEY_c00e2=m

You’ll recognize the vendor (0483) and the device (572d) that I found earlier. It’s important to use uppercase hex codes for vendor and product in the hwdb file – but not for the scan codes, which should be lowercase. The values on the right have to be lowercase, and correspond to the KEY_LEFT, KEY_RIGHT and KEY_M values from /usr/include/linux/input-event-codes.h. (You can pick any of the KEY_ values from there.) Then a quick bit of Linux magic to update the hardware database:

$ sudo systemd-hwdb update
$ sudo udevadm trigger

…and… exactly the same as before. I got the volume control displayed when I turned the knob.

After scratching my head and doing some searching, I happened on https://catswhisker.xyz/log/2018/8/27/use_vecinfinity_usb_foot_pedal_as_a_keyboard_under_linux/ which gave me the clue I needed. My knob was being detected, but not as a keyboard – so it wasn’t being used as a keyboard input device.

So I created this file:

$ cat /etc/udev/rules.d/99-usb-knob.rules
ACTION=="add|change", KERNEL=="event[0-9]*", 
 ATTRS{idVendor}=="0483", ATTRS{idProduct}=="572d",
 ENV{ID_INPUT_KEYBOARD}="1"

(That’s all on one line on my machine.) You’ll recognize the vendor and product ID from earlier, using lowercase for the hex this time. I added ID_INPUT_KEYBOARD to the list of attributes for this device.
Unplug the device, plug it back in, and hooray! I’m doing what I wanted to! When I turn the knob left, I go left. When I turn the knob right, I go right. When I press the knob, “m” shows up on the screen.

Now I just need to install an SDR program… and an SDR….


Building Signalink Cables

June 13, 2016

Many of us have sound card interfaces for our radios that use the standard RJ-45 plug on one end and a custom connector for the radio on the other. If you’ve got more than one radio, it’s sometimes possible to buy additional interface cables. That can get pricey, though – and depending on the connector on your radio, an interface cable might no longer be available.

For many rigs it’s possible to buy a connector that ends in bare wire fairly cheaply. I hit eBay and found a cheapie Kenwood connector for $2.49 (“4 Wire Speaker Mic Cable for Baofeng UV5R Kenwood TK-240”).

Kenwood style connector with bare ends

While holding one of these in my hand, I noticed that the individual wires in the radio cable were roughly the same diameter as the wires in cat-5 network cable.

Before doing anything else I wrote down which wires connected to which pins on the radio. All of the wires in my cable had different colors, which made identification a lot easier. Next, I determined which pin in the RJ-45 plug should be connected to which wire. This varies depending on the radio connector and sound card interface you use. In my case, green went to the 2.5mm plug tip aka speaker, red went to the 3.5mm ring aka mic, black went to the 3.5mm sleeve aka PTT, and white went to the 2.5mm sleeve aka ground. I found this Tigertronics page useful.

Close-up of connector and wire

After that, I cut the interface cable straight across with diagonal cutters. My cable came with an integrated strain relief, and I cut that off as well. Then I carefully removed a little more than half an inch (about 13mm) of the cable jacket, being careful not to nick the wires inside.

Cable together but before crimping

I arranged the wires in the correct order they’d need to be into the RJ-45 plug. The wires were solid core, so I was able to spread them more or less into position. Next I inserted the cable into the RJ-45 plug, being careful to slip each wire into the appropriate channel. One or two recalcitrant wires needed persuasion with a pin to find the right home.

Once all the wires were in their channels, I pushed hard on the cable to ensure all the wires were as far forward in the plug as they would go. At this point I crimped the RJ-45 plug. There are two nice things about an RJ-45 crimp: there’s no need to strip the wires (the plug bites down on them to make the connection), and the crimp forces part of the plug’s shell against the cable, which keeps it in place.

Then came the moment of truth: I tested continuity of each pin on the connector. Success!

Completed cable

The radio’s connectors were in the right place, and I had a professional-looking interface cable for a radio that needed it.


Turning a Raspberry Pi 2 into a packet station

March 31, 2016

I keep thinking it’s a good idea for emergency communications to have a packet station. Since I’m cheap, I didn’t want to get extra hardware – instead I wanted to use what I had. Luckily, Dire Wolf is better than any hardware packet decoder out there. Here’s how I got a working packet station on a Pi 2 running Raspbian Jessie Lite.

  1. Connect the radio to the Pi. In my case, I had a spare Signalink SL1+ hanging around which made things easier. I  bought a Syba CMedia USB sound card to talk from the Pi to the Signalink, and a Kenwood speaker mic cable to talk from the Signalink to the radio. Here’s a useful hint: the diameter of the wires in the speaker mic cable are roughly the same as the diameter of the wires in regular Ethernet cable – meaning that you can (if you’re careful) strip the outer jacket, put the inner wires in the right places of an RJ-45 connector and crimp direct to them with no soldering at all.
  2. Download and build Dire Wolf. Instructions for doing so on a Pi are here. I mounted my home directory on a networked drive to make life easier.
  3. Configure Dire Wolf with your callsign (I used the SSID -15 after my call) and sound card. Be sure to avoid the “# ADEVICE – plughw:1,0” line – it looks a lot like the correct “ADEVICE  plughw:1,0” line, but takes input from stdin instead of the sound card.
  4. Run “direwolf” and tune the radio to 144.390 (APRS). Make sure you’re decoding packets. You might have to go to alsamixer and adjust input/output. Mine ended up being 51 for speaker, 29 and 12 for mic. Also adjust the radio volume so it’s not too high or too low. (Hit F6 to get your sound card, then F5 to see all devices. I’m not sure which mic I was using; I had two – a stereo and a mono one. The mono one was 29, the stereo one was 12.) It’s probably a good idea to turn off the squelch on the radio as well.
  5. sudo apt-get install ax25-tools ax25-apps
  6. Edit /etc/ax25/axports and set one line to:
    vhf   mycall-15 1200 255 2 VHF link (1200 bps)
  7. Make sure all the other lines in axports have # in front of them (it doesn’t like blank lines).
  8. Run “direwolf -p” to get the KISS port. It will show up as something like /dev/pts/2. Once it’s running, move to another terminal window.
  9. Change frequency to the freq that you’re going to use.
  10. sudo /usr/sbin/kissattach /dev/pts/2 vhf (your IP address in AMPR 44.0.0.0)
  11. sudo /usr/sbin/kissparms -p vhf -t 200 -s 20 -r 64 -l 50 -f n
    These parameters took a little tweaking. If the transmit delay (-t) was too big, things timed out. If it was too small, things stepped on each other. I had to adjust transmit tail delay as well (-l). I found this page useful for some values.
  12. sudo route del -net 44.0.0.0 netmask 255.0.0.0
    (because I’d set up a route beforehand and needed to nuke it)
  13. sudo /sbin/route add -net 44.0.0.0 netmask 255.0.0.0 dev ax0
  14. ping -i 10 (someone else’s IP who also has a machine on the AMPR 44.0.0.0 net)
  15. Assuming that works, you might want to apt-get install telnet telnetd talk talkd and try to log into your friend’s machine or have your friend log into yours.
  16. Last but not least: I ran into problems with arp. I increased the arp timeout in /etc/sysctl.d/local.conf:net.ipv4.neigh.default.base_reachable_time_ms=1200000

Controlling T-Series Vector Impedance Analyzer from a netbook

March 21, 2016

I’ve been using the T-Series Vector Impedance Analyzer program from Times Technology Company to control my dad’s MFJ-226 antenna analyzer (aka Times Technology T200). Both he and I ran into problems with it on a netbook with a screen size of 1024×600. Most of the “interesting” buttons were off screen!

The right solution for Windows 7 and higher appears to be to allow scaling of the netbook’s screen. That’s documented here. But for those unfortunates who are stuck on Windows XP or for some other reason can’t increase the screen size, here’s what I ended up doing.

To start/stop the connection to the analyzer:

  • Click the “SWR” button
  • Press [Tab] [Tab] [Cursor back <-] [Space]

To load a file from the analyzer:

  • Click the “Save to File” button
  • Click “Cancel”
  • Press [Tab] [Tab] [Tab] – you are now on the file to load
  • Press [Backspace] [Backspace] [Backspace] [Del] [Del] [Del]
  • Enter the file you want to load from the analyzer (1, 3, 12, etc.)
  • Press [Shift-Tab]
  • Press [Enter]
  • Now you can click the “Save to file” button and save the data.

Foot Switch for the Insane

December 28, 2014

I’ve been looking for a foot switch for a while now to act as a PTT for a radio. Yesterday at a thrift store, I came across the Koino KH-8012:
SPST Foot switch with NEMA 1-15 plug

Do you see anything wrong with this picture? That is indeed a SPST foot switch with a NEMA 1-15 plug on the end. I can’t think of any reason that you would want to do that – the switch is rated at 15 A / 125 V AC (as well as 14 V DC), so it’s not like it was meant for a European destination where the corresponding socket wouldn’t be found in the wild.

The label is a lie too – it’s normally open, and conducts when closed. It is not SPDT: there are only two conductors coming out of the switch.

I’ve since rendered it safe and unable to short out household wiring by cutting off the NEMA 1-15 and adding a 1/4 inch jack instead.

All I can find about this switch is that it’s available from China and Vietnam, and costs USD $6.49 each when bought in large lots. Any idea what this was originally used for, or why on earth anyone would want to terminate it in a way that seems designed to blow fuses? Leave a comment.


Repairing a MFJ-259B Antenna Analyzer

November 29, 2014

I’ve had an MFJ-259B antenna analyzer for a while, and for the most part it’s been pretty good. However, in the last few months I’ve seen it intermittently give me really high SWR as opposed to normal SWR.

Usually, that means there’s a break in a transmission line somewhere, but I kept seeing it on different lines. Curiously, it usually went away when I touched the antenna connector.

I wondered if I was adding capacitance or something to the system, but finally I realized it happened when the feedline cable pulled down on the analyzer. It was just a break between the antenna connector and the analyzer.

I took the analyzer apart, re-soldered the SO-239 and I was back in business.

Here’s what I learned when I took the antenna analyzer apart:

  1. Take the battery cover off first (two screws on the bottom)
  2. Next, unscrew both sides (four screws on each side)
  3. At this point, you’ll have access to the battery compartment. Take out the two top batteries and the two bottom batteries (don’t need to take out the rest).
  4. You’ll see four screws that hold the battery compartment to the analyzer. Actually, that’s a lie – only the two right-side screws hold the battery compartment to the analyzer. The left screws are screwed into Delrin insulators. Don’t unscrew the left screws or the insulators will drop off and you’ll have to look under the table for them. Just unscrew the right screws (top and bottom).
  5. At this point you can move the battery compartment to the side, and get easy access to the SO-239 connector. Don’t lose the lock washers that are under the screws.
  6. I suspect they used lead-free solder to solder the connector, which is more prone to cracking than 60/40. I upped the heat a little and mixed in some 60/40 solder to make it more durable.
  7. At this point you can put the 4 batteries back in and test with a dummy load and a good cable. I did this and verified my problems with mystery SWR were gone.
  8. Put things back together in the reverse order that you took them apart.

Editions of ARRL Hints and Kinks Books

February 14, 2014

I’ve been looking around the web for a list of the ARRL “Hints & Kinks for the Radio Amateur” editions. I wasn’t able to find one, so here’s what I know:

Edition Year Orig Price Preface
First volume (unlabeled) 1933 0.50
Volume Two 1937 0.50 Ross A. Hull (VK3JU operating as ARRL station W1MK/W1AW?)
Volume Three 1945 0.50 unsigned
Volume Four 1949 1.00 A. L. Budlong (W1BUD)
Volume Five 1954 1.00 A. L. Budlong (W1BUD)
Volume Six 1959 1.00 A. L. Budlong (W1BUD)
Volume Seven 1965 1.00 John Huntoon W1LVQ
8th Edition 1968 1.00 John Huntoon W1LVQ
9th Edition 1973? 1974? 2.00
Edition Year Orig Price Editor
10th Edition 1978 4.00 Stuart Leland W1JEC
11th Edition 1982 4.00 Charles L. Hutchinson K8CH, Stuart B. Leland W1JEC, Larry D. Wolfgang WA3VIL
12th Edition 1989 5.00 Charles L. Hutchinson K8CH, David Newkirk AK7M
13th Edition 1992 10.00 David Newkirk WJ1Z
14th Edition 1997 12.00 Robert Schetgen KU7G, David Newkirk W9VES
15th Edition 2000 12.00 Larry Wolfgang WR1B
16th Edition 2003 15.95 Steve Ford WB8IMY
17th Edition 2004 17.95 Dana G. Reed W1LC
18th Edition 2012 22.95 Steve Ford WB8IMY

All the editions from 10 on are the large format (8 x 11 inches) and have an editor; the editions from 1 through 9 are 6 x 9 inch and sometimes have the writer of the preface/forward listed. N4MW has info about the earlier editions as well as other classic ARRL publications.

Thanks also to James Smith for clarifications about three of the earlier editions.