Skip to content

Commit

Permalink
Complete refactor. More GPS data is parsed and sorted in MQTT, rework…
Browse files Browse the repository at this point in the history
…ed structure, logging
  • Loading branch information
askrejans committed Jan 19, 2025
1 parent 9d4ed15 commit 5e37d79
Show file tree
Hide file tree
Showing 7 changed files with 718 additions and 340 deletions.
8 changes: 5 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gps-to-mqtt"
version = "0.1.3"
version = "0.2.0"
edition = "2021"

[dependencies]
Expand All @@ -10,3 +10,5 @@ paho-mqtt = "0.12.5"
futures = "0.3.31"
lazy_static = "1.5.0"
gumdrop = "0.8.1"
thiserror = "2.0.11"
log = "0.4.25"
125 changes: 77 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,46 @@

## Overview

This Rust project is designed to read data from a USB GPS dongle in NMEA-0183 format over serial, parse the data, and publish relevant information to an MQTT broker. Please note that this implementation is not complete, and it has only been tested with a specific USB dongle. There is no guarantee that it will work with different devices.
This Rust project serves as a bridge between GPS hardware and MQTT-based systems, enabling real-time GPS data integration into IoT and telemetry applications. It reads NMEA-0183 format data from USB GPS dongles, processes the various sentence types, and publishes parsed information to configurable MQTT topics.

## Features
### Key Capabilities

- Reads GPS data from a USB dongle in NMEA-0183 format.
- Parses relevant sentences and dispatches them to specialized functions.
- Publishes parsed information to MQTT topics.
- **GPS Data Processing**: Reads and parses standard NMEA-0183 sentences including position, speed, course, and satellite information
- **Real-time MQTT Publishing**: Converts GPS data into structured MQTT messages with configurable topics and QoS levels
- **High-Frequency Updates**: Optional support for 10Hz update rates on compatible u-blox GPS modules
- **Flexible Configuration**: TOML-based configuration for serial port settings, MQTT broker details, and topic customization

## Warning
### Hardware Compatibility

### Device Compatibility
While the software supports standard NMEA-0183 protocols, it has been primarily tested with the TOPGNSS GN800G GPS module (M8030-KT chipset). The 10Hz high-frequency mode specifically targets u-blox compatible devices. Users should exercise caution when using untested GPS hardware. Use it at your own risk!

This project has been tested with a specific USB dongle (TOPGNSS GN800G with M8030-KT chipset). Compatibility with other devices is not guaranteed.
### Use Cases

### 10Hz Mode Toggle
- Vehicle tracking systems
- Fleet management solutions
- IoT data collection
- Navigation applications
- Telemetry systems integration

There is a toggle that switches the dongle to 10Hz mode, which might be dangerous on other devices. Use this feature at your own risk. Binary commands with u-blox undocumented commands are pushed to the device for this operation.
> **Note**: This is an ongoing development project. While functional, it may require adjustments for specific use cases or hardware configurations. Contributions and feedback are welcome to improve compatibility and features.
## Features

## Main Logic
- 📡 Reads NMEA-0183 GPS data from USB GPS dongles
- 🔄 Support for 10Hz GPS update rate (u-blox devices only)
- 🛰️ Parses multiple NMEA sentence types:
- GSV (Satellites in View)
- GGA (Fix Information)
- RMC (Recommended Minimum Data)
- VTG (Track & Speed)
- GSA (Overall Satellite Data)
- GLL (Geographic Position)
- TXT (Text Transmission)
- 📊 Publishes parsed data to MQTT topics

The main parsing logic is contained in the `gps_data_parser` module, specifically in the `process_gps_data` function. This function takes a slice of bytes representing received data, converts it to a string, and dispatches the relevant sentences to specialized parsing functions.
### 10Hz Mode Toggle

There is a toggle that switches the dongle to 10Hz mode, which might be dangerous on other devices. Use this feature at your own risk. Binary commands with u-blox undocumented commands are pushed to the device for this operation.

## Build Instructions

Expand All @@ -42,9 +61,13 @@ To build the project, follow these steps:
cd gps-to-mqtt
```

4. Create an `example.settings.toml` file in the same directory as the executable. Refer to `example.settings.toml` for configuration options.
4. Copy the [example.settings.toml] file to [settings.toml] in the same directory as the executable. Modify [settings.toml] as needed for your configuration:

```bash
cp example.settings.toml settings.toml
```

5. Build the project:
5. Build the project in release mode:

```bash
cargo build --release
Expand All @@ -56,18 +79,6 @@ To build the project, follow these steps:
./target/release/gps-to-mqtt
```

## Configuration

Copy and modify the `example.settings.toml` file to configure the project. Ensure that this file is in the same directory as the executable.

## Dependencies

- [serialport](https://crates.io/crates/serialport) - 4.3.0
- [config](https://crates.io/crates/config) - 0.13.4
- [paho-mqtt](https://crates.io/crates/paho-mqtt) - 0.12.3
- [futures](https://crates.io/crates/futures) - 0.3.30
- [lazy_static](https://crates.io/crates/lazy_static) - 1.4.0

## Project Structure

- `src/config.rs`: Module for loading project configuration.
Expand All @@ -76,31 +87,49 @@ Copy and modify the `example.settings.toml` file to configure the project. Ensur
- `src/serial_port_handler.rs`: Module for setting up and reading from the serial port.
- `src/main.rs`: Entry point for the application.

## Usage

1. Clone the repository and build the project using the provided build instructions.
2. Ensure that the USB GPS dongle is connected to the system.
3. Copy and modify the `example.settings.toml` file to configure the project.
4. Run the executable as described in the build instructions.

## MQTT data format

MQTT data is stored under configured topic as 3 letter codes:

- CRS - course in degrees
- TME - GMT time in HH:MM:SS format
- DTE - date in dd.mm.YYYY format
- LAT - latitude
- LNG - longitude
- SPD - speed in km/h
- ALT - altitude in m
- QTY - fix quality

![image](https://github.com/askrejans/gps-to-mqtt/assets/1042303/37bf6b97-259f-4e90-bbb2-71de8d6aeef1)
## MQTT Data Format

MQTT data is stored under the configured base topic (default: `/GOLF86/GPS/`) using 3-letter codes as subtopics.

### Core GPS Data
- `CRS` - Course/heading in degrees (0-359°)
- `TME` - GMT time in HH:MM:SS format
- `DTE` - Date in dd.mm.YYYY format
- `LAT` - Latitude in decimal degrees (±90°)
- `LNG` - Longitude in decimal degrees (±180°)
- `SPD` - Ground speed in km/h
- `ALT` - Altitude in meters above sea level
- `QTY` - GPS fix quality (0=invalid, 1=GPS fix, 2=DGPS fix)

### Additional Speed Formats
- `SPD_KTS` - Speed in knots
- `SPD_KPH` - Speed in kilometers per hour

### Satellite Information
- `SAT/GLOBAL/NUM` - Total number of satellites in view
- `SAT/GLOBAL/ANTSTATUS` - Antenna status
- `SAT/GLOBAL/PF` - Position fix status
- `SAT/GLOBAL/GNSS_OTP` - GNSS chip configuration

### Per-Satellite Data
Under `SAT/VEHICLES/{PRN}/` where PRN is the satellite ID:
- `FIX_TYPE` - Fix type (Not Available, 2D, 3D)
- Full satellite info string containing:
- PRN number
- Satellite type (GPS/GLONASS/Galileo/BeiDou)
- Elevation angle
- Azimuth angle
- SNR (Signal-to-Noise Ratio)
- In View status

### Geographic Position (GLL specific)
- `GLL_TME` - Time from GLL sentence
- `GLL_LAT` - Latitude from GLL sentence
- `GLL_LNG` - Longitude from GLL sentence

## Pre-Built Packages

There are also pre build packages, that combines three individual components: [Speeduino-to-MQTT](https://github.com/askrejans/speeduino-to-mqtt), [GPS-to-MQTT](https://github.com/askrejans/gps-to-mqtt), and [G86 Web Dashboard](https://github.com/askrejans/G86-web-dashboard) in one system with predefined services.
There are also pre build packages (outdated), that combines three individual components: [Speeduino-to-MQTT](https://github.com/askrejans/speeduino-to-mqtt), [GPS-to-MQTT](https://github.com/askrejans/gps-to-mqtt), and [G86 Web Dashboard](https://github.com/askrejans/G86-web-dashboard) in one system with predefined services.

You can quickly get started by using pre-built packages available for both x64 and Raspberry Pi 4 (ARM) architectures:

Expand Down
40 changes: 15 additions & 25 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ pub struct AppConfig {

// The base topic of MQTT where data is pushed
pub mqtt_base_topic: String,

// Optional: Path to the configuration file
pub config_path: Option<String>,
}

/// Load application configuration from a TOML file.
Expand All @@ -35,13 +32,11 @@ pub struct AppConfig {
/// # Returns
/// Returns a `Result` containing either the `AppConfig` struct with the loaded configuration or an error message.
pub fn load_configuration(config_path: Option<&str>) -> Result<AppConfig, String> {
let mut settings = Config::default();

if let Some(path) = config_path {
settings = load_from_path(path)?;
let settings = if let Some(path) = config_path {
load_from_path(path)?
} else {
settings = load_default_paths()?;
}
load_default_paths()?
};

Ok(AppConfig {
port_name: settings
Expand All @@ -56,7 +51,6 @@ pub fn load_configuration(config_path: Option<&str>) -> Result<AppConfig, String
mqtt_base_topic: settings
.get_string("mqtt_base_topic")
.unwrap_or_else(|_| "default_topic".to_string()),
config_path: config_path.map(|p| p.to_string()),
})
}

Expand Down Expand Up @@ -96,8 +90,6 @@ fn load_from_path(path: &str) -> Result<Config, String> {
/// * `Ok(Config)` - If a configuration file is successfully loaded from any of the default paths.
/// * `Err(String)` - If there is an error loading the configuration from all default paths.
fn load_default_paths() -> Result<Config, String> {
let mut settings = Config::default();

if let Ok(exe_dir) = std::env::current_exe() {
let exe_dir = exe_dir.parent().unwrap_or_else(|| Path::new("."));
let default_path = exe_dir.join("settings.toml");
Expand All @@ -106,27 +98,25 @@ fn load_default_paths() -> Result<Config, String> {
.add_source(File::with_name(default_path.to_str().unwrap()))
.build()
{
settings = config;
return Ok(config);
}
}

if let Err(_) = Config::builder()
if let Ok(config) = Config::builder()
.add_source(File::with_name(
"/usr/etc/g86-car-telemetry/gps-to-mqtt.toml",
))
.build()
.and_then(|config| {
settings = config;
Ok(())
})
{
if let Ok(config) = Config::builder()
.add_source(File::with_name("/etc/g86-car-telemetry/gps-to-mqtt.toml"))
.build()
{
settings = config;
}
return Ok(config);
}

if let Ok(config) = Config::builder()
.add_source(File::with_name("/etc/g86-car-telemetry/gps-to-mqtt.toml"))
.build()
{
return Ok(config);
}

Ok(settings)
Ok(Config::default())
}
Loading

0 comments on commit 5e37d79

Please sign in to comment.