Persistent serial ports on Ubuntu

June 30, 2020

Last time I rebooted my file server (which has a couple of serial devices attached), it picked the wrong ones for /dev/ttyUSB0 and /dev/ttyUSB1. That meant the speeds (which I’d configured in minicomrc files) were wrong.

I needed to make the serial ports persistent. Again with the udev rules!

Luckily, there was a handy page at:

with what I needed to know. I ended up adding two rules in /lib/udev/rules.d/99-serial-aliases.rules:
# portal
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="AM00NPHK", MODE="0666", SYMLINK+="ttyPortal"
# wally
SUBSYSTEMS=="usb", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="0130D901", MODE="0666", SYMLINK+="ttyWally"

I got these values by running lsusb -v (which in truth gives you everything you need to know) and:
udevadm info -a -n /dev/ttyUSB0 | grep serial | head -1
udevadm info -a -n /dev/ttyUSB1 | grep serial | head -1
on my two serial ports. (I could also grep idProduct and idVendor, but lsusb gave me the right ones already.)

Then delete (sigh) /lib/udev/rules.d/99-serial-aliases.rules~ that Emacs left over. Cause it gets read if you don’t. Sigh. I always forget.

After that a quick:
sudo udevadm control --reload-rules
sudo udevadm trigger

and I had my /dev/ttyPortal and /dev/ttyWally. Now to fix up those minicom configs…

Fixing a busy CP210x serial device on Ubuntu

April 5, 2020

I had the opportunity to reorganize my local machines. As part of that, I wanted to plug my firewall’s serial port into USB serial and pop that into my server. I have a CP2104 serial device that I bought with my PC Engines apu2 which I use for a firewall.

The USB serial device worked fine when plugged into my Windows 8 laptop, but I want my server to be able to connect to my firewall even when the network is down. Because the apu2 is headless, it’s nice to have something that’s plugged into a monitor when I need to fix things.

I plugged the USB serial port in, and tried to connect to my firewall with minicom. I got this instead:

$ minicom 
minicom: cannot open /dev/ttyUSB0: Device or resource busy

So, time to look at who has /dev/ttyUSB0 open:

$ sudo lsof | grep ttyUSB0
gpsd 416 root 3u CHR 188,0 0t0 176 /dev/ttyUSB0

Ok, why is gpsd holding /dev/ttyUSB0 open? It’s true I have a GPS attached to my server, but that runs as /dev/ttyACM0 and has nothing to do with /dev/ttyUSB0. Hmm… time to search and find this in the gpsd FAQ: Why does GPSD open non-GPS USB devices?

That made me suspicious. See, gpsd is trying to be too friendly – and to do that, it opens a whole bunch of possibly GPS devices even if they’re not GPS devices! Could that be my problem?

$ lsusb
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 008 Device 003: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x UART Bridge / myAVR mySmartUSB light
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 040: ID 1546:01a7 U-Blox AG 
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

Hmm… let’s take a peek in /lib/udev/rules.d/60-gpsd.rules:

... blah blah blah...
# Cygnal Integrated Products, Inc. CP210x Composite Device (Used by Holux m241 and Wintec grays2 wbt-201) [linux module: cp210x]
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="gps%n", TAG+="systemd", ENV{SYSTEMD_WANTS}="gpsdctl@%k.service"
... more blah...

So in an effort to detect the Holux m241 and Wintec grays2 wbt-201, it’s matching the vendor and product ID of my CP2104 serial device as well! Luckily, I don’t have any of those GPS devices, so a quick snip:

# Cygnal Integrated Products, Inc. CP210x Composite Device (Used by Holux m241 and Wintec grays2 wbt-201) [linux module: cp210x]
# commented out because it interferes with Andrew's PC Engines 2104 USB serial cable
#ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="gps%n", TAG+="systemd", ENV{SYSTEMD_WANTS}="gpsdctl@%k.service"

was all it took. Now my USB serial device shows up as a serial device, and is not held open by a GPS daemon.

I have to disagree with the GPSD FAQ’s statement, “It’s not a problem we can solve with clever programming, the devices simply don’t yield enough information about themselves to avoid conflicts.” Err, no… clever programming would have the user run through an install procedure which involved plugging the device in, and detecting the device. Then they could update the udev rules so that only the device that a user owned was stolen by gpsd, and not all serial devices on the planet that happened to match a vendor/product ID that they knew about.

Programing with a PICkit 2 on Linux

January 8, 2020

I recently bought a PICkit 2 clone (the ICA03) from Programming it has been something of a challenge, mostly because Microchip no longer really supports Linux programming of PIC chips.

First, I needed to get pk2cmd. There are lots of pointers to Microchip’s website, but all of them go to 404s. What I ended up using is a github repo: After reading the threatening license agreement, I cloned the repo and built it with make then sudo make install.

git clone
cd pk2cmd/pk2cmd
make linux
sudo make install

Even after installing PK2DeviceFile.dat in /usr/share and including /usr/share/pk2 on the path (ugh) per the instructions, I still wasn’t able to use it from anywhere except the directory I built it from. At some point I’ll need to look into that.

I plugged the PIC into the ZIF socket with the marking near the top, and made sure the selection switch was on 28-40.

Next, I took my .hex file and stuffed it in ~/pk2cmd/pk2cmd/. Then:

~/pk2cmd/pk2cmd$ sudo pk2cmd -P
Auto-Detect: Found part PIC16F886.

Yay! Let’s try writing the file:

~/pk2cmd/pk2cmd$ sudo ./pk2cmd -PPIC16F886 \
 -f my_hex_file.hex -MPC -Y

(I didn’t include IE on the -M switch because I think my hex file has ID and EEPROM memory in it. -Y does the verification.)

PICkit 2 Program Report
8-1-2020, 23:12:12
Device Type: PIC16F886

Program Succeeded.
PICkit 2 Verify Report
8-1-2020, 23:12:12
Device Type: PIC16F886

Verify Succeeded.

Operation Succeeded


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.



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 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-
S: Sysfs=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.7/2-1.7.2/2-
U: Uniq=2070363C4250
H: Handlers=kbd event8 
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)
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

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 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",

(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….

Finding out which disk is which on Ubuntu

June 9, 2019

My file server often has various disks swapped in and out. It can get confusing which /dev/sd? corresponds to which drive. While reading the man page of findfs, I stumbled on this:

The complete overview about filesystems and partitions you can get for example by:
  lsblk –fs
  partx –show

These three things (along with judicious use of e2label and the other *label commands) is going to make my life a lot easier!

Fixing Brother printing on Ubuntu

March 15, 2019

Since moving to Ubuntu 16.04.3, printing to my Brother MFC-J650DW printer has been broken. I can print fine, but it’s always offset by a bit, never where it should be on the page.

Turns out this is a bug and there’s a workaround. So I don’t forget next time I set up printers:

lpadmin -p "Brother_MFC_J650DW" -o pdftops-renderer-default=pdftops

Let me know (mail me) when there’s an error

September 5, 2018

I’ve got a shell script where I’d like to know when an error happens. Typically when that happens, something gets written to stdout or stderr – and I’d like to see that. But when things are just peachy, I don’t want to be bothered.

Here’s an easy way to achieve that. At the beginning of my script, I have:


(Did I mention this is a script for a weather station? Yep.)

Then in the body of the script, I have:

/usr/local/bin/do-the-thing > ${WEATHEROUT} 2> ${WEATHERERR}
/usr/local/bin/do-the-other-thing >> ${WEATHEROUT} 2>> ${WEATHERERR}

Finally, at the end of the script, there’s:

if [ -s ${WEATHERERR} -o -s ${WEATHEROUT} ]; then
   cat ${WEATHEROUT} ${WEATHERERR} | /usr/bin/mail -s "Weather command error" me@myaddr

That’s all!

What’s up with mod_security and User-Agent? (406 Not Acceptable)

May 1, 2018

So… what’s the deal with mod_security and User-Agent? I tried to browse to using Lynx, and was greeted with this 406 error:

                            Not Acceptable

   An appropriate representation of the requested resource / could not be
   found on this server.

   Additionally, a 406 Not Acceptable error was encountered while trying
   to use an ErrorDocument to handle the request.

A few searches and I discovered this was due to Apache mod_security.

I don’t get it. Why would you exclude based on User-Agent? That’s something that can be changed at will by any program that decides to be nefarious. This seems like security theatre rather than real security.

Even in the best case, this kind of “security” just turns into a red queen’s race to the bottom where everything will now lie about what it is because someone screwed up a config file somewhere.

And so I’ve started lying (in my .bashrc):

alias lynx='lynx -useragent="Mozilla/5.0 (X11; Ubuntu Lynx; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0"'

Damn, that’s stupid.

Setting a user and group for Samba drives

April 14, 2018

The last time I tried to set a user and password on my Samba drive, I ran into a strange problem: even though my credentials were correct, I was still using the user and group I was logged in as, rather than the one I’d stored with the Samba config.

Luckily, these days there’s an easy way around it in /etc/fstab:

//mysvr/music /music cifs uid=1000,gid=1000,credentials=/etc/samba/credentials/mysvr


Simply adding the uid= and gid= lines fixed up the problem for me.

What the hell, Gnome? Canonical?

January 1, 2018

Today when I started working on my desktop, I saw a crash in gom-media-tracker. What? Why is there a media tracker on my desktop?

I learned this is part of Gnome (what? Why is there a media tracker in Gnome?) and it’s included in Ubuntu 16.04 LTS (why is Canonical including tracking software in Ubuntu?)

Screw that. I removed it.

$ sudo apt remove gnome-online-miners
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages will be REMOVED:
  gnome-documents gnome-online-miners gnome-photos ubuntu-gnome-desktop
0 upgraded, 0 newly installed, 4 to remove and 0 not upgraded.
After this operation, 6,472 kB disk space will be freed.
Do you want to continue? [Y/n] y
dpkg: warning: files list file for package 'fonts-gfs-complutum' missing; assuming package has no files currently installed
(Reading database ... 493027 files and directories currently installed.)
Removing ubuntu-gnome-desktop (0.58.3) ...
Removing gnome-documents (3.18.3-0ubuntu0.16.04.1) ...
Removing gnome-photos (3.18.2-1) ...
Removing gnome-online-miners (3.14.3-1ubuntu2) ...
Processing triggers for libglib2.0-0:amd64 (2.48.2-0ubuntu1) ...
Processing triggers for man-db (2.7.5-1) ...
Processing triggers for hicolor-icon-theme (0.15-0ubuntu1) ...
Processing triggers for gnome-menus (3.13.3-6ubuntu3.1) ...
Processing triggers for desktop-file-utils (0.22-1ubuntu5.1) ...
Processing triggers for bamfdaemon (0.5.3~bzr0+16.04.20160824-0ubuntu1) ...
Rebuilding /usr/share/applications/bamf-2.index...
Processing triggers for mime-support (3.59ubuntu1) ...

Now that I’ve done that, I don’t appear to have lost anything I use. (Why is an optional packages forcing other packages out?)

I don’t want random tracking software on my machine., you suck. Gnome, you suck. Canonical, you suck.