ICOM IC-9700 and IC-7300 coexisting in Linux

I’ve just added an IC-9700 to the desk alongside the IC-7300, they look very nice together and very similar. Unfortunately the Audio and Serial devices also look very similar in Linux!

Both radio sound cards appear as PCM2901 making it hard to differentiate between them, and my previous udev rule to create a symlink for the IC-7300 ttyUSB device on /dev/ic7300 picked up the 9700 instead.

My previous udev serial rule created a symlink to /dev/ttyUSBx from /dev/ic7300 when a device with the 7300’s serial adapter idVendor and idProduct was seen like so:

SUBSYSTEM==”tty”, ATTRS{idVendor}==”10c4″, ATTRS{idProduct}==”ea60″ SYMLINK+=”ic7300″

This worked fine until the 9700 was plugged in as it also has the same idVendor and idProduct number. We can narrow this down easily as another available attribute is “serial” which contains the radios unique serial number along with its name.

We can get the serial numbers using udevadm  (or better with lsusb below) against each of the /dev/ttyUSBx devices, the 9700 has two serial devices, the first works fine with hamlib for control but not sure what the second serial device at the moment.

$ udevadm info –attribute-walk –path=/sys/bus/usb-serial/devices/ttyUSB4 | grep IC-
ATTRS{serial}==”IC-9700 13000000 A”

$ udevadm info –attribute-walk –path=/sys/bus/usb-serial/devices/ttyUSB5 | grep IC-
ATTRS{serial}==”IC-9700 13000000 B”

$ udevadm info –attribute-walk –path=/sys/bus/usb-serial/devices/ttyUSB1 | grep IC-
ATTRS{serial}==”IC-7300 03000000″

An easier way to get the serial numbers is by running lsusb as root as follows, thanks to PA3MET for the pointer!

# lsusb -vvvvv | egrep "9700|7300"
iSerial 3 IC-9700 13000000 B
iSerial 3 IC-9700 13000000 A
iSerial 3 IC-7300 03000000

We can take these and create appropriate udev rules for adding symlinks for the radios serial devices including the unique serial numbers. Here is an extract from my /etc/udev/rules.d/99-hamlib.rules file:

SUBSYSTEM==”tty”, ATTRS{idVendor}==”10c4″, ATTRS{idProduct}==”ea60″, ATTRS{serial}==”IC-7300 03000000″, SYMLINK+=”ic7300″

SUBSYSTEM==”tty”, ATTRS{idVendor}==”10c4″, ATTRS{idProduct}==”ea60″, ATTRS{serial}==”IC-9700 13000000 A”, SYMLINK+=”ic9700a”

SUBSYSTEM==”tty”, ATTRS{idVendor}==”10c4″, ATTRS{idProduct}==”ea60″, ATTRS{serial}==”IC-9700 13000000 B”, SYMLINK+=”ic9700b”

Take care if copying the above text as the quotation marks are displaying incorrectly in WordPress. If you copy it as is you will need to replace them all with proper quotation marks.

A reload of udev rules with “udevadm trigger” and we have our serial devices available from convenient symlinks so no chasing about ttyUSB device names:

$ ls -l /dev/ic*
lrwxrwxrwx 1 root root 7 Apr 4 14:27 /dev/ic7300 -> ttyUSB1
lrwxrwxrwx 1 root root 7 Apr 4 14:27 /dev/ic9700a -> ttyUSB4
lrwxrwxrwx 1 root root 7 Apr 4 14:27 /dev/ic9700b -> ttyUSB5

Next up is the sound cards. I use quite a lot of different programs and need to easily switch and adjust my audio inputs/outputs using pavucontrol. The problem here is both audio devices have the same name, PCM2901, meaning I can’t easily tell what sound card belongs to what radio.

There’s no way to differentiate the sound cards like with the serial above as they return the exact same information and attributes. The only way to differentiate them from what I can see is with the physical USB port they are plugged in to. This is fine here as it’s a desktop and they will remain plugged in to the same ports. If you plug the radios in to a different USB socket you will need to update the paths again. 

We can list the devices with the following command, this shows both the IC-7300 and IC-9700 sound cards. I’ve removed some other sound cards from the output, as we’re just looking for the “Burr-Brown_from_TI_USB_Audio_CODEC” entries here

$ pacmd list-sources | egrep “name:|sysfs”

name: <alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo.2.monitor>
sysfs.path = “/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.4/3-2.4:1.0/sound/card4”
name: <alsa_input.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo.2>
sysfs.path = “/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.4/3-2.4:1.0/sound/card4″
name: <alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo.3.monitor>
sysfs.path = “/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.4/3-3.4:1.0/sound/card5”
name: <alsa_input.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo.3>
sysfs.path = “/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.4/3-3.4:1.0/sound/card5″

We are looking to extract the unique portion from device paths above, here we can see the paths differ at 3-3/3-3.4/3-3.4 and 3-2/3-2.4/3-2.4. We will want to identify which entry belongs to which radio so run it with the USB disconnected then connected to identify which device matches which radio.

We can then run the following to apply a device description of IC9700 to the IC-9700 sound card source and sink which will show against the audio device in pulse applications instead of PCM2901.

The 3-3.4 below is referencing the end of the USB port discovered above.

pacmd update-source-proplist $(pacmd list-sources | egrep “name:.*Burr-Brown*|3-3.4” | grep -B 1 sysfs.path | grep name | sed “s/.*<\(.*\)>/\1/” | grep -v monitor) device.description=IC9700

pacmd update-sink-proplist $(pacmd list-sinks | egrep “name:.*Burr-Brown*|3-3.4” | grep -B 1 sysfs.path | grep name | sed “s/.*<\(.*\)>/\1/”) device.description=IC9700

And the same for the IC7300 with 3-2.4:

pacmd update-source-proplist $(pacmd list-sources | egrep “name:.*Burr-Brown*|3-2.4” | grep -B 1 sysfs.path | grep name | sed “s/.*<\(.*\)>/\1/” | grep -v monitor) device.description=IC7300

pacmd update-sink-proplist $(pacmd list-sinks | egrep “name:.*Burr-Brown*|3-2.4” | grep -B 1 sysfs.path | grep name | sed “s/.*<\(.*\)>/\1/”) device.description=IC7300

The audio devices should now be available using the name we set above in pavucontrol and other applications. This can be put in a script to run manually or at system startup.

WSJT-X alerts to MD-380 with the openSPOT HTTP API

DMR SMS alerts using the SharkRF openSPOT with Node-RED

I recently acquired a SharkRF openSPOT for use as a hotspot to connect to the Brandmeister DMR network with my MD-380 DMR radio as I have no easily accessible repeaters nearby to use for DMR.

I have nothing but good things to say about this device, it works very well, the UI is simple to use, reported bugs are fixed very quickly and new features added with new firmware. The icing on the cake is it is a very accessible device with a HTTP and UDP API to interact with! I’ve only toyed with some features in the HTTP API but happy with what I’ve seen so far.

The first use I came up with for it was receiving DMR SMS messages to my MD-380 from my existing WSJT-X & Node-RED setup. The status-dmrsms API allows us to receive and send SMS messages over the local RF link to our connected DMR radio by specifying its DMR ID. This functionality works exactly as described in the API documentation now and if you follow it you will get a beer.


BEER from the SharkRF openSPOT HTTP API

In order to get this working in to Node-RED a flow was needed to handle authentication. As described in the Login Process, we need to hash our openSPOT password with a provided token to get a digest for use in all communication to the API, this digest is valid for 60 minutes.

The flow below shows the authentication process it as set up at the moment.

Node-RED openSPOT API Login

Node-RED openSPOT API Login

The inject at the beginning just sends a time stamp, which is unused, to start this flow off on Node-RED start-up and every 30 minutes thereafter. After the login is posted some global variables are set with the login status, the token and the digest if authentication was successful. This should hopefully tick away to ensure we will have a valid digest to hand at all times.

The posting of messages is easy and exactly as documented in the API description. With the digest already in a global variable from the login process above, we take any text input, limit it to 75 characters, convert to UTF16BE HEX and post it in the correct format for our radio. The full flow including the message input from WSJT-X is pictured below.

Full flow for using the openSPOT API

Full flow for using the openSPOT API

The inject function in the send flow is just there for testing purposes to insert a test message manually and the success function at the end just writes the status to the debug console.

With the above all set up we just wait for the DX to light up our DMR radio with an SMS message, the image below shows this on an earlier version of the same flow.

WSJT-X alerts to MD-380 with the openSPOT HTTP API

WSJT-X alerts to MD-380 with the openSPOT HTTP API

Alerts from Node-RED via Twitter or IRC might be easier but at least with the above it is all contained on the RF side and doesn’t need the Internet 🙂

It has been running for a few days now and seems to be working fine. I’ll try and wrap it all up in a more easily deployed function if I get the time but if anyone wants the nasty code before then just drop me a line.