Using a Cheap USB GPS Module (VK-162) with Python

I recently picked up a VK-162 USB GPS module from AliExpress for about $8. It’s a simple, plug-and-play GPS receiver that presents itself as a serial device (on Windows, it shows up as a COM port) as opposed to most cheap GPS units, which only offer a UART output.

This same device is also known as the “G-Mouse USB GPS dongle”

Surprisingly, when I went looking for examples on how to actually read and parse data from this module, I didn’t find much. So I put together a small script that works reliably, and I’m sharing it here in case it helps someone else.


Features

  • Built in Ublox G6010 / G7020 GPS chipset.
  • 50 channel GPS L1 frequency C/A Code.
  • Superior sensitivity up to -162dBm.
  • Built-in WAAS/EGNOS/MSAS Demodulator without any additional hardware.
  • Low power consumption.
  • Waterproof design.
  • USB interface with a 2m long cable.
  • Magnetic base.
  • Compatible with Linux & Windows.

Specifications

  • Tracking sensitivity: -160dBm
  • Acquisition sensitivity: -146dBm
  • Cold start time: 32s [average]
  • Warm start time: 32s [average]
  • Hot start time: 1s [average]
  • Recapture Time: 0.1s [average]

How It Works

When you connect the VK-162 to your PC and open the COM port (e.g., with PuTTY), you’ll see NMEA sentences streaming in, such as:

$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48

Although that’s valid GPS data, it’s not very user-friendly and requires further processing to be human-readable.

NMEA definitions:

$GPVTG → Course and speed over ground.
$GPRMC → Recommended Minimum GPS data (time, status, lat/lon, speed, date).
$GPGGA → Fix data (quality, satellites, altitude).
$GPGSA → DOP (dilution of precision) and active satellites.
$GPGLL → Geographic position.


Parsing NEMA Sentences

With Python and a couple of libraries (pyserial and pynmea2), you can parse these NMEA sentences and present them in a more readable way.

Note: I added a simple configuration flag for RAW_OUTPUT so you can choose whether to display the raw NMEA data alongside the parsed output, in case it’s not working as expected.

Install dependencies before running:

pip install pyserial pynmea2

Python Script

The example Python script will parse the NEMA sentences into a human readable output.

Configuration

At the top of the script, in the Config section, there’s configuration variables to set the the PORT & if RAW_OUTPUT status:

# --- Config ---
PORT = "COM9"
RAW_OUTPUT = False   # Set True to show raw NMEA sentences
BAUDRATE = 9600
# ---------------

You MUST set the correct PORT prior to running the script.

In most cases, the device will be listed as a USB Serial Device in your Windows Device Manger.

Example Code

import serial
import pynmea2

# --- Config ---
PORT = "COM9"
RAW_OUTPUT = False   # Set True to show raw NMEA sentences
BAUDRATE = 9600
# ---------------

def main():
    ser = serial.Serial(PORT, baudrate=BAUDRATE, timeout=1)
    print(f"Listening to GPS on {PORT}... (Ctrl+C to stop)")

    while True:
        try:
            line = ser.readline().decode('ascii', errors='replace').strip()
            if not line.startswith('$'):
                continue

            if RAW_OUTPUT:
                print(f"RAW: {line}")

            try:
                msg = pynmea2.parse(line)

                if isinstance(msg, pynmea2.types.talker.RMC):
                    print(f"[RMC] Time: {msg.datestamp} {msg.timestamp}")
                    print(f"      Status: {'Active (fix)' if msg.status == 'A' else 'Void (no fix)'}")
                    print(f"      Lat/Lon: {msg.latitude}, {msg.longitude}")
                    print(f"      Speed (knots): {msg.spd_over_grnd}, Course: {msg.true_course}")
                    print("-" * 40)

                elif isinstance(msg, pynmea2.types.talker.GGA):
                    print(f"[GGA] Fix Quality: {msg.gps_qual}")
                    print(f"      Satellites: {msg.num_sats}")
                    print(f"      Altitude: {msg.altitude} {msg.altitude_units}")
                    print("-" * 40)

                elif isinstance(msg, pynmea2.types.talker.GSA):
                    print(f"[GSA] Mode: {msg.mode}, Fix Type: {msg.mode_fix_type}")
                    print(f"      PDOP: {msg.pdop}, HDOP: {msg.hdop}, VDOP: {msg.vdop}")
                    print("-" * 40)

            except pynmea2.nmea.ParseError:
                continue

        except KeyboardInterrupt:
            print("Exiting...")
            break

if __name__ == "__main__":
    main()

Example Output

Here’s what the script looks like in action.

Debug mode on (RAW_OUTPUT = True)

Listening to GPS on COM9... (Ctrl+C to stop)
RAW: $GPRMC,233029.00,A,3548.56817,N,08229.00148,W,0.045,,160925,,,A*68
[RMC] Time: 2025-09-16 23:30:29+00:00
      Status: Active (fix)
      Lat/Lon: 35.8094695, -82.483358
      Speed (knots): 0.045, Course: None
----------------------------------------
RAW: $GPVTG,,T,,M,0.045,N,0.084,K,A*2E
RAW: $GPGGA,233029.00,3548.56817,N,08229.00148,W,1,08,1.05,762.2,M,-32.5,M,,*68
[GGA] Fix Quality: 1
      Satellites: 08
      Altitude: 762.2 M
----------------------------------------
RAW: $GPGSA,A,3,31,26,16,03,09,27,04,08,,,,,1.95,1.05,1.64*0A
[GSA] Mode: A, Fix Type: 3
      PDOP: 1.95, HDOP: 1.05, VDOP: 1.64
----------------------------------------
RAW: $GPGSV,3,1,09,03,16,218,21,04,79,254,33,08,30,176,31,09,42,311,33*72
RAW: $GPGSV,3,2,09,16,71,030,14,26,32,051,26,27,49,139,34,28,01,100,*7D
RAW: $GPGSV,3,3,09,31,25,088,30*46
RAW: $GPGLL,3548.56817,N,08229.00148,W,233029.00,A,A*79
RAW: $GPRMC,233030.00,A,3548.56819,N,08229.00153,W,0.058,,160925,,,A*68
[RMC] Time: 2025-09-16 23:30:30+00:00
      Status: Active (fix)
      Lat/Lon: 35.80946983333333, -82.48335883333333
      Speed (knots): 0.058, Course: None
----------------------------------------

Debug mode off (RAW_OUTPUT = False)

Listening to GPS on COM9... (Ctrl+C to stop)
[RMC] Time: 2025-09-16 23:31:17+00:00
      Status: Active (fix)
      Lat/Lon: 35.80946866666667, -82.48338616666666
      Speed (knots): 0.112, Course: None
----------------------------------------
[GGA] Fix Quality: 1
      Satellites: 08
      Altitude: 757.2 M
----------------------------------------
[GSA] Mode: A, Fix Type: 3
      PDOP: 1.94, HDOP: 1.05, VDOP: 1.63
----------------------------------------

No GPS Fix

If your device has not yet completed the GPS fix, you will see Status: Void.
—————————————-
Raw: $GPGSV,1,1,01,16,,,25*78
Raw: $GPGLL,,,,,,V,N*64
Raw: $GPRMC,,V,,,,,,,,,,N*53
Time: None None
Status: Void (no fix)
Lat/Lon: 0.0, 0.0
Speed (knots): None, Course: None
—————————————-

It can take up to 180 seconds to achieve a fix from a cold boot; make sure the device is near a window and has a line of sight to the outside sky.


Conclusion

For less than $10, this little VK-162 USB GPS dongle works great with Python. By parsing the NMEA output with pynmea2, you can easily log your position, altitude, and satellite info, or even integrate it into larger projects like mapping or robotics.

Sometimes the cheap modules on AliExpress end up being surprisingly useful — and with a bit of code, you can unlock their potential.


Additional Resources

https://www.instructables.com/GPS-Raspberry-Pi

Omada Controller Page

Omada Software Controller – RaspberryPi Install Guide

The process of installing the Omada Software Controller on a Raspberry Pi is reasonably straightforward, with a few quirks due to the platform not being officially supported by TP-Link Omada.


Requirements

Raspberry Pi 4(or 5) with 4GB+ Memory
Compatible TP-Link Omada Router(ER605)


SD Card Setup

Download the Ubuntu Server 24.04 image.

Write the Ubuntu image to your MicroSD card.

Raspberry Pi Imager – This is my preferred tool for setting up a Raspberry Pi OS image.

Balena Etcher – Popular cross-platform tool for writing images to USB/SD Cards.

USB Imager – Lightweight Open Source image writing tool; works with standard user-level accounts in Windows.


Raspberry Pi Setup

Once you have configured your MicroSD card with the Ubuntu Server image, login to the Raspberry Pi via SSH, or the local console. If you didn’t use the Raspberry PI Imager to pre-configure your login details, you’ll have to set a new password after the first login(Default login is ubuntu/ubuntu).

Double-check that all unattended upgrades are complete.

Before we begin, double-check that any unattended upgrades are complete.

Update existing packages:
sudo apt-get update
sudo apt-get -yV upgrade 

Install Utilities
sudo apt install nano wget screen curl

Install Java:
sudo apt install openjdk-17-jre-headless
sudo apt install jvsc

Fix Java Home error:
ln -s /usr/lib/jvm/java-17-openjdk-arm64 /usr/lib/jvm/default-java

Install MongoDB 4.x

MongoDB changed their architecture in 4.19 release, making further versions incompatible with the Raspberry Pi.

To avoid compatibility issues, we’ll be installing the older release of MongoDB 4.18 manually:

wget https://repo.mongodb.org/apt/ubuntu/dists/focal/mongodb-org/4.4/multiverse/binary-arm64/mongodb-org-server_4.4.18_arm64.deb
sudo dpkg -i mongodb-org-server_4.4.18_arm64.deb

You may get an error that Package libssl1.1 is not installed:

 mongodb-org-server depends on libssl1.1 (>= 1.1.0); however:
  Package libssl1.1 is not installed.

If this happens, install the missing package manually, and then repeat the install for MongoDB:

wget http://launchpadlibrarian.net/741613665/libssl1.1_1.1.1f-1ubuntu2.23_arm64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2.23_arm64.deb
sudo dpkg -i mongodb-org-server_4.4.18_arm64.deb

Install Omada Network Controller

Now that OpenJDK & MongoDB are installed, we’re ready to Install the Omada Network Controller.

Download the latest Omada x64.deb package from the Omada software center.

sudo dpkg -i omada_v5.15.24.18_linux_x64_20250630184434.deb

It may take a few moments to complete the installation. The network controller will start up automatically as part of the install process.

Installation Example Output:
root@omadapi:~# dpkg -i omada_v5.15.24.18_linux_x64_20250630184434.deb
Selecting previously unselected package omadac.
(Reading database ... 59655 files and directories currently installed.)
Preparing to unpack omada_v5.15.24.18_linux_x64_20250630184434.deb ...
JRE 17.0.15 is greater than 8 and JSVC 1.0.15 is less than 1.1.0
Unpacking omadac (5.15.24.18) ...
Setting up omadac (5.15.24.18) ...
Install Omada Controller succeeded!
==========================
current data is empty
Omada Controller will start up with system boot. You can also control it by [/usr/bin/tpeap]. 
check omada
Starting Omada Controller. Please wait.
............         
Started successfully.
You can visit http://localhost:8088 on this host to manage the wireless network.

Once the installation is complete, you’ll be able to access the Omada Network Controller in your browser

https://ip.add.re.ss:8043
http://ip.add.re.ss:8088

You should now be staring at the Omada Setup screen.

Meshtastic – TDeck Plus – FancyUI

The T-Deck Plus is a great LoRa product made by Lillygo that includes all the necessary hardware to use the Meshtastic framework in an easy-to-use form factor.

The problem is the default UI is… awful. It’s designed for small OLED read-only devices, and thus does not take advantage of the Blackberry-eqsue nature of the T-Deck device.

The solution is the “FancyUI” firmware; a development work-in-progress build that offers a much more user friendly UI, and shows some of the potential what the device can become in the future.

You can easily build your own firmware image using GitPod, an online build environemtn. All you need is a free GitHub account to get started.

Create a new Work Space

Access GitPod, and allow GitPod to access your GitHub account to create your new Work Space:

https://gitpod.io/new#https://github.com/meshtastic/firmware

Build the Firmware

Now that the Work Space is up-to-date, we’re ready to build the latest Meshtastic Firmware for our T-Deck-Plus.

First make sure you have the latest files; if this is your first build, the files should be up to date already.

git pull

Next we’ll make a new branch to work with where we build our firmware

git switch tft-gui-work

Now we can init the sub modules that we need

git submodule update --init

And finally build our firmware binaries, this part will take a few minutes(more like 10)

pio run -e t-deck-tft 

Once the build is complete, you’re ready to download the firmware.bin from the path specified in the terminal – /workspace/firmware/.pio/build/t-deck-tft/firmware.bin

To access the files, use the file browser on the left-hand-bar. Right click on the file in the file browser, and click Download to download the firmware.bin to your local PC.

Once we have the firmware.bin on our PC, we’re ready to shut-down the Work Space and flash our T-Deck.

Build Command Summary:

git pull
git switch tft-gui-work
git submodule update --init
pio run -e t-deck-tft 

Flashing the T-Deck

Use the M5Stick Launcher to install the new firmware.bin – https://bmorcelli.github.io/M5Stick-Launcher

Proceed with the initial T-Deck install, then use the Web or SD flasher methods to update the device.

If you are using the Web based method; first login to your Access Point on the T-Deck, then navigate to the IP address provided, and login with the username & password shown.

Click on OTA Update and select the firmware.bin file downloaded from GitPod to install.

Wait for the update to complete and reboot the T-Deck.

Upon the next boot, you should be staring at the T-Deck “FancyUI”, where you’ll need to configure the Region & usual Meshtastic Settings.

Online.net IPv6 Setup Script

Someone on LowEndTalk recently posted a great script to automatically configure IPv6 on Online.net’s dedicated servers.  Since I’ve recently been using one of their 2EUR Kidéchire specials as a Deluge & SABnzbd box I found this quite useful.

Script Usage:

This script is designed to be run on an Online.net Dedicated Server, it has been tested on:

  • Debian 7
  • Ubuntu 14.04
  • CentOS 7
  • Proxmox VE w/ OVZ (see Issue #1)

Updated 01-23-2017

wget www.sonicboxes.com/scripts/online_net_ipv6_dhclient_Jan17.tar.gz
tar -xf online_net_ipv6_dhclient_Jan17.tar.gz
cd ipv6-dhclient-script
chmod 755 ipv6_dhclient_online_net.sh
./ipv6-dhclient-script.sh <interface> <address block> <subnet> <duid>

Example:

All of the required information can be found on your Online.net Network configuration page (console.online.net >> Server >> Network configuration).  It should look like this:

Online.net IPv6 Example Settings

In this example, we’ll be adding the subnet 2001:0bc7:5555:101::/64 to our dedicated server via the following command:

./ipv6-dhclient-script.sh eth0 2001:0bc7:5555:101:: 64 00:04:01:07:c7:b8:2f:13:d5:a8

Next we’ll want to add some addresses from the /64 block to the server:

/sbin/ifconfig eth0 inet6 add 2001:0bc7:5555:101::1
/sbin/ifconfig eth0 inet6 add 2001:0bc7:5555:101::2

Your server should now have outgoing IPv6 connectivity and you should be able to ping the assigned address, in this example – 2001:0bc7:5555:101::1, from an outside network.

Source: GitHub – LowEndTalk Thread

BitTorrent – Deluge WebUI Install Script

Deluge is a BitTorrent client written in python based on libtorrent. Deluge is my favorite  BitTorrent client because it’s got some great features like:

09/08/16 – Updated for Deluge 1.3.13 – Tested on  Debian 8/Ubuntu 16.04

  • It’s Fast!
  • Full Encryption Support
  • Cross-Platform Support
  • Thin Client Mode
  • Great Web-UI
  • Plugin System

Not only have I found Deluge to be faster than many of the other BitTorrent clients that I have tried but, it’s also simple to setup and reasonably lightweight in terms of resources. Packaged versions of Deluge can be a bit old and not up to snuff, so I’ve written a  simple bash script that will automatically install the latest version of Deluge from it’s source.  The entire process, on a freshly installed VPS, takes approximately 5 minutes.

Read More

Observium Client Setup Script

Observium is a neat PHP based system monitoring platform that I’ve been using lately, it’s fairly easy to install (Observium + Nginx/PHP-FPM install guide Coming Soon!) and provides a large variety of easy to read graphs of system information.

Observium is an autodiscovering SNMP based network monitoring platform written in PHP which includes support for a wide range of network hardware and operating systems including Cisco, Windows, Linux, HP, Dell, FreeBSD, Juniper, Brocade, Netscaler, NetApp and many more.

From the Official Observium Wiki

After the initial task of Observium host installation and configuration, you need install and configure SNMP daemon on all of your client servers, aka all the servers that you want to monitor.  This can be quite annoying if you’re monitoring more then a handful of servers, thus I have taken the time to write bash script for installation of the SNMP daemon on a client server.

Read More

LXDE + NoMachine Low Memory Auto-Install Script

This is a little script I’ve been tinkering with for a bit now, it installs LXDE + NoMachine for remote desktop access.  It’s a minimalist script based around low ram usage (perfect for LEBs!), it just installs a bare desktop, a web browser(iceweasel) and a means to access it, any other programs i.e. VPN, torrent client, etc.. must be installed by the user.

Update 1/05/15: An Android/iOS client is now available for NoMachine, please see the links below for the APK/iOS store link.  I’ll be adding a configuration guide for the Android client shortly.

Update 3/24/14: Fixed an issue with package and client URLs not resolving, also fixed a bug in the libcairo section of the script.  Please continue to report your feedback.

Usage:

wget http://www.sonicboxes.com/nx/autolxde.sh
chmod 755 autolxde.sh
./autolxde.sh

At the end of the script you will be asked if you want to create a user,

You cannot login to NoMachine as root
If you already have a non-root user, you don’t have to add another.

Add a new user? (y/n)

If you have already created a user enter n, if you have not created a user and only have the root then you should enter y and create a user at this time.  You will be prompted you the username and desired password. Read More