Necromancy and the Garmin Nuvi 1390

My father asked me to update the maps on his Garmin Nuvi 1390 on Friday evening.  This did not go so well.  Using Garmin Express, I applied the updates, unplugged it when it said, and it sat there with a black screen saying “Loading maps.” and that was all.  Nothing else.

Whoops.  Anyway, a lot of digging revealed that what was going on is that the GPS couldn’t boot, likely due to corruption in the filesystem.  So here’s how to fix that.

You first need Garmin Cure 3.  This is kicking around in various places, but you can get it at that link.  You also need the firmware image for the current firmware you have installed, which you can find here.  And you need a tool to reformat the mass storage (if this is required), which you can use RMPrepUSB for.  I’ll assume you have Garmin Connect or something installed so you have the USB drivers.

Getting access to the Mass Storage

After installing all the above, you’ll need to fire up Garmin Cure 3, point it at the firmware image you downloaded, then put it into CURE mode, and start it.  At this point the GPS should be turned off and unplugged.  Wait until it says it’s ready to load, then click the Updater button to launch the updater.

Now, you need to get the GPS into pre-boot mode.  On the Nuvi 1390, that involves holding your finger on the top left corner of the screen and turning it on.  Keeping your finger on the screen, plug in the USB cable, wait for Windows to make the “device connected” sound, and click to let the updater work.

Fixing Mass Storage Issues

Assuming you now get firmware up, what will happen is that you have the CURE firmware on.  This firmware will not load, but when you boot the GPS up and plug it into your computer, you should get the GPS appear as a mass storage device.  Let’s assume it does.  You can now copy off anything you want, try and fix it, or like in my case, reformat it.

If you reformat, you can use RMPrepUsb to do so.  Follow the instructions at this link to do so.  Notably, the Nuvi 1390 doesn’t care if the filesystem is blank when you first boot it.  Doing this will wipe anything you downloaded or anything factory loaded such as car icons, the sample Cyclops database and other things like that.

Restoring the Original Firmware and Updating

After that’s done, unplug the GPS, turn it off.  Repeat the above CURE process, but this time select ORIGINAL as the option.  This will put normal firmware on.  You should now be able to boot your device.  Run Garmin Express and update it.  Tell it to reinstall the maps, which will take a while, and you should be (mostly) back to normal.

Installing a POI Database

Restoring like this will destroy the sample Cyclops safety camera database.  That’s OK, we’ll replace it.  Using a website like PoiDB, get hold of a POI database in GPX format.  You will now need to install Garmin’s POI Loader, then point the Loader at the GPX you downloaded.  Follow the prompts and it will insert that POI database in GPI format into your GPS, and your safety camera alerts should be restored.

Notably though, and this is annoying, the safety camera alerts won’t be colorized for your speed or anything, but they’ll be there.  If you have actually paid for Cyclops you can probably just get the proper Cyclops DB installed by just using Garmin Express and don’t need to use a community-made POI database like I did.

Good luck.

AussieBroadband CheckMK Plugin

I’ve recently changed my ISP to AussieBroadband.  Since I’m now working under a quota, I want a way to monitor my quota in CheckMK.  Enter a bit of python.  If you want to use this, you’ll need to adjust the hashbang to suit your actual OMD site, and then pass a parameter which is your username:password to get onto your ABB account.

# Parses AussieBroadband Quota Page to generate a CheckMK alert and stats pages

import requests
import HTMLParser
import re
import time
import sys

# Create a basic HTML parser for fetching the Total Used up/down and Data Left fields
class UsageParser(HTMLParser.HTMLParser):
    # Always given in MB
    quota_up = None
    quota_down = None
    quota_left = None

    # This flag determines the state of the parser
    usage_state = None

    def handle_data(self, data):
        if data == "Total Used":
            # This flag is followed by an upload value
            self.usage_state = "upload"
        elif data == "Data Left":
            # This flag is followed by the quota left value
            self.usage_state = "remain"
        elif self.usage_state is not None:
            m = re.match("([0-9\.]+) ([KMGT])B",data)
            if m:
                value = float(
                unit =

                # Convert unit to MB
                if unit == "K":
                    value = value/1024.0
                elif unit == "M":
                    value = value
                elif unit == "G":
                    value = value*1024.0
                elif unit == "T":
                    value = value*1024.0*1024.0

                # Put unit in correct pool based on value and reset parser state
                if self.usage_state == "upload":
                    self.quota_up = value
                    # Uploads are followed by downloads
                    self.usage_state = "download"
                elif self.usage_state == "download":
                    self.quota_down = value
                    self.usage_state = None
                elif self.usage_state == "remain":
                    self.quota_left = value
                    self.usage_state = None


status = 0
statustext = "OK"

    creds = sys.argv[1].split(":")

    payload = {
        'login_username': creds[0],
        'login_password': creds[1],
        'submit': 'Login'

    # Fetch the usage page and parse it
    r ="", data=payload)
    parser = UsageParser()

    # Derive some parameters for the check
    total = parser.quota_left + parser.quota_up + parser.quota_down
    critthresh = 0.10*total
    warnthresh = 0.25*total

    # Determine the status of the check
    if parser.quota_left < critthresh:
        status = 2
        statustext = "CRITICAL"
    elif parser.quota_left < warnthresh:
        status = 1
        statustext = "WARNING"

    # Format the output message
    print "{7} - {1} MB quota remaining|left={1};{2};{3};0;{4}, upload={5}, download={6}".format( \
        status, \
        int(parser.quota_left), \
        int(warnthresh), \
        int(critthresh), \
        int(total), \
        int(parser.quota_up), \
        int(parser.quota_down), \

    print "UNKNOWN - Unable to parse usage page!"
    status = 3
    statustext = "UNKNOWN"


Enjoy.  It’s pretty quick and dirty, but it works.  You put this into your site’s local/lib/nagios/plugins directory, then add it as a classical check.

Darktable on Windows through WSL

EDIT:  No longer required.  Since Darktable 2.4.0, there’s an official native Windows installer for it.  Use that instead.  Easy!

With the Windows Subsystem for Linux (WSL) now being much more stable and useable, it turns out it’s possible to install Darktable on Windows with very little fuss.

This will require you to have Windows 10, and also to have at least the 1709 Fall Creator’s Update (run winver and see your version, it should be 1709 or higher).

Follow the instructions here to install WSL, and then go ahead and install Ubuntu from the Windows store.  Don’t bother starting a prompt yet, we have more to do.

Next, you’ll need an X server of some type, to display graphical UI from Ubuntu apps on the screen.  Unless you have something else, I suggest you install VcXsrv, it’s straightforward to install and run.  When running this, just select all the defaults and go ahead.  This will give you an X server on :0, which we will use in a moment.

Now, start up Ubuntu, then type nano ~/.profile and press enter.  Enter the following text down the bottom,

# set display
if [ "$DISPLAY" == "" ]; then
  export DISPLAY=localhost:0

Press Ctrl-O and Y in order to save.  Now exit that Ubuntu window and start it up again.  If you type echo $DISPLAY you should see the variable above printed out.  This tells programs in your Ubuntu window how to find your X server.

Next, go to this PPA repository, and install Darktable like this;

sudo add-apt-repository ppa:pmjdebruijn/darktable-release
sudo apt-get update
sudo apt-get install darktable

Wait a bit, and Darktable will be installed.  You can now run it by simply typing darktable into that prompt.

No mess, no fuss.  Enjoy.


Stopping DNS leakage with pfSense

I’ve recently changed my core router over from OpenWRT to pfSense.  I was pretty happy with OpenWRT, but I wanted something more powerful since it was running in a VM anyway.

A few days ago, CloudFlare announced their new service.  This is a public DNS service very much like Google’s DNS service, with a notable difference.  It supports TLS.

Why should you care?  Because DNS requests are normally not encrypted, and therefore visible to your ISP to record, use for research / marketing purposes, or even (in the case of some nefarious actors) manipulate or change.  Running DNS over TLS prevents that, by encrypting your DNS traffic so that it can’t be manipulated or collected.

In this post, we’ll be configuring pfSense to do three things – provide a local standard unencrypted port 53 DNS resolver which uses CloudFlare’s encrypted service on the WAN end, and then set up a NAT redirect so any attempts on the internal network to use port 53 DNS servers outside the network instead are intercepted and resolved by the internal resolver.  Lastly, it will also make sure that it blocks any outbound requests to port 53 just to be sure.

NOTE:  There’s one piece here I haven’t figured out yet.  How to pin a cert for the DNS endpoints listed here, so it’s not perfect.  When I figure that out, I’ll edit this post.

Let’s get started.

Configuring the pfSense Local Resolver

In pfSense, go to Services -> DNS Resolver, then put the following block into Custom Options:

ssl-upstream: yes
do-tcp: yes
    name: "." 
    forward-addr: 2606:4700:4700::1111@853
    forward-addr: 2606:4700:4700::1001@853

You will also need to make sure that the DNS Query Forwarding option is NOT selected, otherwise the above settings will conflict.  It’s OK to set the resolver to listen on all interfaces, since the firewall rules on the WAN will prevent Internet hosts from using your resolver anyway.  Follow the prompts, then test it with something like;

dig @yourrouter.local

You should see a resolve against your router’s local DNS resolver that works.  If you really want, use Diagnostics -> Packet Capture, and capture port 853 to verify that requests are being triggered.

Redirect all DNS requests to outside DNS servers to pfSense

Follow the article you can find here.  You will need to do this once for each of your interfaces (in my case, LAN, DMZ, and VPN).  Obviously don’t configure this for the WAN interface.  This then causes any requests to addresses that are not on your internal network to be resolved through the local pfSense resolver (which goes out to port 853 anyway).

To test this, try and dig something against an IP that you know is not internal and is not a DNS server.  It should work, since the request will be NATted.  Something like;

dig @

Assuming that’s all fine, you should now be able to configure a broad block rule to bar all outbound port 53.

Block all outbound non-encrypted DNS

This shouldn’t really be required if the NAT rule is working, but we’ll do it anyway to be sure we’re stopping any DNS leaks.

In pfSense, go to Firewall -> Rules, and for the WAN interface, define a new rule at the top of the list.  This rule should use these settings;

Action: Block
Interface: WAN
Address Family: IPv4+IPv6
Protocol: TCP/UDP
Source: any
Destination: any
Destination Port: DNS (53)
Description: Block outbound insecure DNS

After doing this, verify that you can still resolve against the local resolver (your router’s IP), and that you can still resolve against what seems to be external resolvers (eg,  You should also check that when you do so that nothing passes on the WAN interface on port 53.

If that all passes, you’re done.   It’s up to you if you use the ‘Block’ target or the ‘Reject’ target.  Block causes a simple timeout if something hits 53 (which shouldn’t happen anyway), Reject causes an immediate fail.


Ubuntu replaces /bin/sh with Dash

Trap for young players.  Ubuntu replaces the default interpreter for /bin/sh from Bash to Dash.  This was done for performance reasons, but certain scripts really don’t like this.  You can easily change it back with;

sudo dpkg-reconfigure dash

Information about this can be found here.  This was done quite a long time ago, apparently, but for whatever reason scripts that ultimately wind up calling the PHP 7.1 interpreter while under Dash break badly under some circumstances (resulting in PHP segfaulting).



Homemade HOTAS Desk Mount

I decided to make up a desk mount for my HOTAS, so that I could position the stick in a more natural location without having to gorilla-arm it.  The design is very straightforward – it’s a drill vice with a couple of pieces of steel bracket and soft-grip vice jaws to avoid marking the desk.  Total cost under $30.

If you want to replicate this, the spacing of the holes for the Warthog stick are 60mm apart, and are M4 metric screws.  Remember to leave enough clearance so you can push the stick the whole way forwards and not catch your fingers on the vice anywhere.

Darktable for Windows using Vagrant

I have an Olympus TG5 camera, which has RAW support for Darktable, but only in the very latest (currently unreleased!) 2.3.0 version.  Since I have Windows, I’ll have to build Darktable directly from source to be able to manipulate it.  Here’s how you can do that.

First, I assume you have Cygwin/X running.  You’ll also need Vagrant installed, along with VirtualBox.  With all that in place, doing the rest is pretty straightforward.  Create a folder, and throw this Vagrantfile into it;

# -*- mode: ruby -*-
# vi: set ft=ruby :

VMBOX = "bento/ubuntu-16.04"
VMHOSTNAME = "darktable"
VMRAM = "1024"


Vagrant.configure("2") do |config|
  # Configure the hostname for the default machine
  config.vm.hostname = VMHOSTNAME

  # Configure the VirtualBox provider
  config.vm.provider "virtualbox" do |vb, override|
    # The default ubuntu/xenial64 image has issues with vbguest additions = VMBOX

    # 1gb RAM, 2 vCPU
    vb.memory = VMRAM
    vb.cpus = VMCPU

    # Configure vbguest auto update options
    override.vbguest.auto_update = false
    override.vbguest.no_install = false
    override.vbguest.no_remote = true

  # Mount this folder as RW in the guest, use this for transferring between host and guest
  config.vm.synced_folder "shared", "/srv/shared", :mount_options => ["rw"]

  # Build the server from a provisioning script (which will build Darktable for us)
  config.vm.provision "shell", inline: <<-SHELL
    # Install essential and optional dependencies
    apt-get update
    apt-get install -y gcc g++ cmake intltool xsltproc libgtk-3-dev libxml2-utils libxml2-dev liblensfun-dev librsvg2-dev libsqlite3-dev libcurl4-gnutls-dev libjpeg-dev libtiff5-dev liblcms2-dev libjson-glib-dev libexiv2-dev libpugixml-dev
    apt-get install -y libgphoto2-dev libsoup2.4-dev libopenexr-dev libwebp-dev libflickcurl-dev libopenjpeg-dev libsecret-1-dev libgraphicsmagick1-dev libcolord-dev libcolord-gtk-dev libcups2-dev libsdl1.2-dev libsdl-image1.2-dev libgl1-mesa-dev libosmgpsmap-1.0-dev

    # Install usermanual and manpage dependencies
    apt-get install -y default-jdk gnome-doc-utils libsaxon-java fop imagemagick docbook-xml docbook-xsl
    apt-get install -y po4a

    # Install this for Cygwin/X to work properly
    apt-get install -y xauth

    # Pull the master repo
    git clone
    cd darktable
    git checkout master

    # Pull the submodules
    git submodule init
    git submodule update

    # Build Darktable
    ./ --prefix /opt/darktable

    # Build documentation
    cd build
    make darktable-usermanual
    make darktable-lua-api
    cd ..

    # Install Darktable
    cmake --build "/home/vagrant/darktable/build" --target install -- -j2

    # Copy documentation into shared area
    cp build/doc/usermanual/*.pdf /srv/shared/

  # This piece here is run when we use 'vagrant ssh' to configure the SSH client appropriately
  if VAGRANT_COMMAND == "ssh"
    config.ssh.forward_x11 = true


Make a shared folder in that folder, and vagrant up followed by vagrant ssh.

Assuming everything is configured correctly, you can then start Darktable with;


And off you go.  You can add some more mounts into the VM as required to share your picture library or whatever with it so you can manipulate it with Darktable.

Rumble Functionality with XB1 Wireless on Steam Link

If you have the newer (model 1708) XBox One Wireless Controller, you can get it to work with a Steam Link on native Bluetooth pretty easily.  But if it’s straight out of the box, the rumble motor won’t work on the controller.

In order to make it work, you’ll need a PC with Windows 10 and the Anniversary Update installed.  Go to the Windows Store and install ‘Xbox Accessories’ by Microsoft.

Then, plug in your XB1 controller with a USB cable into your PC (shut down Steam first).  The PC will then prompt you to update firmware on the controller.  Do so.  You’ll then have to repair it with the Steam Link, but once that’s done rumble functionality should work.

Threading a X-Tek Pure Buckle the Right Way

In case this is useful for anyone besides me, here is the correct way to thread the waist buckle onto a Scubapro X-Tek Pure Harness;

My sincere apologies for the Microsoft Paint drawing.  The long end of the red line represents the end of the webbing that is attached to the rest of the harness, and the short end is the end that you have in your hand.  The rest should be self-explanatory.  Threading it this way results in the buckle pulling tight properly when buckled in, and also means the other end of the webbing can slide over the buckle properly when putting it on.

You don’t want to know how many times I put it on wrong before I figured this out.  I swear that thing exists in n-dimensional space or something.

Converting a bunch of OGG music to MP3, preserving metadata

Quick one.  If you have a heap of OGG music that you want to convert to MP3 format, and also want to conserve the metadata that’s in the music, run this from Ubuntu;

for name in *.ogg; do ffmpeg -i "$name" -ab 128k -map_metadata 0:s:0 "${name/.ogg/.mp3}"; done

Done and dusted!