Open source, low cost, long range (~10km) GPS tracker for rocketry. Uses a 4 layer board designed in Autodesk Fusion. Designed to fit in the smallest rockets at only 22mm x 40mm board size (plus antenna and battery you use).
No radio licence required within Australia. A second board without the GPS or barometer installed can be used as a low-cost base station, powered via USB-OTG from an Android phone running the ORCA Rocket Tracker app.
CORE: WIO-E5 LoRa MODULE
- CPU architecture: STM32WL Cortex M4 32 bit @ 48MHz
- CPU flash memory: 256KB
- SRAM: 64KB
- LoRa radio: Semtech SX1262 (915 MHz)
GPS: UBlox MAX-M10Q
- Max Altitude: 80,000m
- Max G: ≤4
- Max Velocity: 500m/s
- Velocity Accuracy: 0.05m/s
- Heading Accuracy: 0.3 degrees
Altitude Pressure Sensor: TE Connectivity MS5611
- Operating Pressure: 1kPa ~ 120kPa
- Accuracy: ±0.15kPa
- Operating Temperature: -40°C ~ 85°C
Power systems: 3.3V battery with reverse polarity and ESD protection. USB-C battery charging.
ORCA Rocket Tracker is an Android ground station app for amateur rocketry. It connects via USB-C to a LoRa receiver to track your rocket's telemetry and GPS position in real-time.
- Live Telemetry: Monitors Altitude (AGL), RSSI, and Velocity.
- Smart Altimeter: "Start Mission" automatically tares the altitude to 0m (Ground Level).
- Recovery Mapping: Real-time flight path on OpenStreetMap with a "Go to Rocket" button.
- Compass Mode: Shows distance and a directional arrow pointing directly to your rocket.
- Offline Capable: Maps and tracking work without a cellular connection.
- Receiver: LoRa base station board connected via USB-C cable to Android
- Transmitter: Zephyr RTOS (GPS + MS5607 Barometer) board
- Download the ORCA_rocket_tracker.apk to your Android phone.
- Open the file on your Android phone.
- If prompted, allow installation from "Unknown Sources".
- Connect your LoRa receiver via USB-C to your phone.
- When you open the ORCA Rocket Tracker grant permission for it to connect to the USB serial.
Connect debug pins to (STLINK-V3MINIE)[https://www.st.com/resource/en/user_manual/um2910-stlinkv3minie-debuggerprogrammer-tiny-probe-for-stm32-microcontrollers-stmicroelectronics.pdf]
Use a OPENLINK connector to connect the STLINK to the Rocket tracker. The connection is the same as the Wio-E5 mini developer board.
| Wio-E5 STM32WLE5JC | STLINK |
|---|---|
| DIO | SWDIO / TMS / STDC14 PIN 4 / MIPI10 PIN 2 |
| CLK | SWCLK / CLK / STDC14 PIN 6 / MIPI10 PIN 4 |
| GND | GND / GND / STDC14 PIN 7 / MIPI10 PIN 5 |
| RST | RST / T_NRST / STDC14 PIN 12 / MIPI10 PIN 10 |
| 3V3 | VCC / T_VCC / STDC14 PIN 3 / MIPI10 PIN 1 |
Power the STLINK with a USB-C cable.
Power the Wio-E5 STM32WLE5JC Module with another USB-C cable.
The first time you program a Wio-E5 STM32WLE5JC Module you need to remove Read-out Protection (RDP) first with STM32Cube Programmer.
- Download STM32Cube Programmer and run
- Connect the STLINK debugger
- Choose ST-LINK and set Reset Mode -> hardware reset -> Connect
- Open the OB tab -> Change RDP to AA -> Apply
Once saved, this doesn't need to be done again.
The Open Rocket Tracker uses Zephyr RTOS for both the rocket transmitter and base station receiver.
Follow the official Zephyr Getting Started Guide to:
- Install host dependencies
- Install the Zephyr SDK
- Create a workspace and initialize Zephyr with
west
After completing the Zephyr setup, install build and debug tools:
sudo apt install ninja-build openocd stlink-toolsRequired for non-root access to ST-LINK debuggers:
sudo tee /etc/udev/rules.d/99-stlink.rules << 'EOF'
# ST-LINK V2
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666", GROUP="plugdev"
# ST-LINK V2-1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="0666", GROUP="plugdev"
# ST-LINK V3
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374d", MODE="0666", GROUP="plugdev"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374e", MODE="0666", GROUP="plugdev"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374f", MODE="0666", GROUP="plugdev"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3753", MODE="0666", GROUP="plugdev"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3754", MODE="0666", GROUP="plugdev"
EOF
sudo udevadm control --reload-rules
sudo udevadm triggerAdd yourself to the plugdev group:
sudo usermod -aG plugdev $USER
# Log out and back in for group changes to take effectBefore building, you need to activate the Zephyr virtual environment and set ZEPHYR_BASE.
Important: The paths below depend on where you installed Zephyr. Replace /path/to/zephyrproject with your actual Zephyr workspace location (e.g., ~/zephyrproject, /media/user/drive/zephyrproject, etc.).
# Activate the Python virtual environment
source /path/to/zephyrproject/.venv/bin/activate
# Set ZEPHYR_BASE to the zephyr directory inside your workspace
export ZEPHYR_BASE=/path/to/zephyrproject/zephyrAdd these lines to your ~/.bashrc to automatically configure the environment:
# Zephyr RTOS environment (adjust path to your installation)
export ZEPHYR_BASE=/path/to/zephyrproject/zephyr
alias zephyr-env='source /path/to/zephyrproject/.venv/bin/activate'Then reload your shell:
source ~/.bashrcNow you can simply run zephyr-env to activate the environment before building.
Check that your environment is configured correctly:
echo $ZEPHYR_BASE # Should print your zephyr directory path
west --version # Should show west version (requires venv activated)cd zephyr/STM32WL_LORA_tx
west build -b open_rocket_tracker -p always -- -DBOARD_ROOT=$(pwd)cd zephyr/STM32WL_LORA_rx
west build -b open_rocket_tracker -p always -- -DBOARD_ROOT=$(pwd) west flash --runner openocdOr using OpenOCD directly:
openocd -f boards/open_rocket_tracker/support/openocd.cfg \
-c "program build/zephyr/zephyr.elf verify reset exit"west debug --runner openocdSee zephyr/STM32WL_LORA_tx/README.md for more details and troubleshooting.
Both the transmitter and receiver can be configured by editing their prj.conf files. Settings must match on both TX and RX for communication to work.
Edit zephyr/STM32WL_LORA_tx/prj.conf or zephyr/STM32WL_LORA_rx/prj.conf:
# LoRa Configuration
CONFIG_LORA_FREQUENCY=915000000 # Frequency in Hz (915 MHz for Australia/US)
CONFIG_LORA_BANDWIDTH=125 # Bandwidth: 125, 250, or 500 kHz
CONFIG_LORA_SPREADING_FACTOR=12 # SF6-SF12 (higher = longer range, slower)
CONFIG_LORA_TX_POWER=14 # TX power: 2-22 dBm
CONFIG_LORA_TX_INTERVAL_MS=2000 # TX only: interval between transmissions| SF | Range | Data Rate | Min TX Interval | Use Case |
|---|---|---|---|---|
| SF12 | Maximum | ~250 bps | ~1000 ms | Long range, low update rate |
| SF10 | Long | ~980 bps | ~500 ms | Good range, moderate updates |
| SF8 | Medium | ~3125 bps | ~250 ms | Balanced range/speed |
| SF7 | Shorter | ~5470 bps | ~200 ms | Fast updates (4-5 Hz) |
Maximum Range (default):
CONFIG_LORA_SPREADING_FACTOR=12
CONFIG_LORA_TX_INTERVAL_MS=2000Fast Updates (4-5 Hz):
CONFIG_LORA_SPREADING_FACTOR=7
CONFIG_LORA_TX_INTERVAL_MS=200Balanced:
CONFIG_LORA_SPREADING_FACTOR=10
CONFIG_LORA_TX_INTERVAL_MS=500After changing settings, rebuild and reflash both TX and RX:
cd zephyr/STM32WL_LORA_tx && west build -b open_rocket_tracker -p always -- -DBOARD_ROOT=$(pwd) && west flash
cd zephyr/STM32WL_LORA_rx && west build -b open_rocket_tracker -p always -- -DBOARD_ROOT=$(pwd) && west flash| Field | Type | Size | Units / Description |
|---|---|---|---|
| latitude | int32_t | 4 bytes | Degrees × 10⁷ (e.g., -274678530 = -27.467853°) |
| longitude | int32_t | 4 bytes | Degrees × 10⁷ (e.g., 1530279210 = 153.027921°) |
| altitude | int32_t | 4 bytes | Meters (from GPS) |
| timeMs | uint32_t | 4 bytes | HHMMSS format (e.g., 143052 = 14:30:52 UTC) |
| pressure | int32_t | 4 bytes | Pascals (e.g., 101325 Pa = 1013.25 hPa) |
| temperature | int16_t | 2 bytes | Centi-degrees Celsius (e.g., 2350 = 23.50°C) |
| satellites | uint8_t | 1 byte | Number of GPS satellites in view |
| status | uint8_t | 1 byte | Status flags |
| checksum | uint8_t | 1 byte | XOR checksum of all preceding bytes |
The packet uses a simple XOR checksum for error detection. The transmitter calculates the checksum by XORing all bytes in the packet (bytes 0-23), then appends it as byte 24.
How it works:
// Transmitter: Calculate checksum
uint8_t checksum = 0;
for (int i = 0; i < 24; i++) {
checksum ^= packet[i];
}
packet[24] = checksum;
// Receiver: Verify checksum
uint8_t calculated = 0;
for (int i = 0; i < 24; i++) {
calculated ^= packet[i];
}
bool valid = (calculated == packet[24]);Debugging tips:
- If checksum fails, check that TX and RX packet structures match exactly (same field order and sizes)
- Ensure both TX and RX use the same LoRa settings (frequency, bandwidth, spreading factor)
- A valid packet will show data in CSV format; invalid packets print
"Invalid checksum, packet discarded" - The checksum is included in the CSV output as
chksum(hex) for verification
The base station outputs CSV data at 115200 baud:
sats,lat_deg,lon_deg,alt_m,time_ms,pressure_Pa,temp_C,status_hex,checksum_hex,rssi_dBm,snr_dB
Example:
8,-27.467853,153.027921,45,143052,101325,23.50,0x01,0xA5,-85,7
| Column | Type | Description |
|---|---|---|
| sats | int | Number of GPS satellites |
| lat_deg | float | Latitude in decimal degrees |
| lon_deg | float | Longitude in decimal degrees |
| alt_m | int | GPS altitude in meters |
| time_ms | int | UTC time as HHMMSS |
| pressure_Pa | int | Barometric pressure in Pascals |
| temp_C | float | Temperature in degrees Celsius |
| status_hex | hex | Status flags |
| checksum_hex | hex | Packet checksum |
| rssi_dBm | int | Received signal strength (dBm) |
| snr_dB | int | Signal-to-noise ratio (dB) |
The Android app calculates Above Ground Level (AGL) altitude from barometric pressure, which is far more stable and accurate than GPS altitude (±0.5m vs ±10-30m for GPS).
How it works:
- When you press START MISSION, the app captures the current barometric pressure as the reference pressure (P₀)
- As the rocket ascends, air pressure decreases
- The app continuously calculates altitude using the barometric formula:
altitude_agl = 44330 × (1 - (P / P₀)^0.1903)
Where:
- P = current pressure from the rocket's MS5607 barometer (Pascals)
- P₀ = reference pressure captured at mission start (Pascals)
- Result is altitude in meters above your launch pad
Why barometric altitude?
| Method | Typical Accuracy | Drift | Best For |
|---|---|---|---|
| GPS Altitude | ±10-30m | High (especially indoors) | Absolute position |
| Barometric (relative) | ±0.5-1m | Minimal over short periods | AGL / flight altitude |
The raw pressure readings are saved in the mission CSV file, so you can recalculate altitude later if needed.
RSSI (Received Signal Strength Indicator) shows how strong the LoRa signal is. The app validates RSSI values and displays "---" for invalid readings (e.g., during startup or signal loss).
| RSSI (dBm) | Signal Strength | Notes |
|---|---|---|
| -30 to -50 | Excellent | Very close range |
| -50 to -70 | Good | Reliable connection |
| -70 to -90 | Fair | Normal operating range |
| -90 to -110 | Weak | May experience packet loss |
| -110 to -120 | Very Weak | Near maximum range |
| < -120 | Critical | At or beyond LoRa limits |
Note: Valid LoRa RSSI is always negative. Values outside -150 to 0 dBm are filtered as errors.
The app automatically logs all telemetry data to CSV files for post-flight analysis.
CSV File Location:
The mission files are stored in the app's private storage:
/data/data/com.example.orcarockettracker/files/Missions/
How to access your mission data:
-
Using Android File Manager:
- Some file managers can access app data (requires granting permission)
- Look for:
Internal Storage > Android > data > com.example.orcarockettracker > files > Missions
-
Using ADB (Android Debug Bridge):
# List mission files adb shell ls /data/data/com.example.orcarockettracker/files/Missions/ # Pull all missions to your computer adb pull /data/data/com.example.orcarockettracker/files/Missions/ ./missions/ # Pull a specific mission adb pull /data/data/com.example.orcarockettracker/files/Missions/Mission_20241201_143052.csv
-
Using Android Studio Device Explorer:
- Connect phone via USB with debugging enabled
- View > Tool Windows > Device Explorer
- Navigate to:
data > data > com.example.orcarockettracker > files > Missions
CSV File Format:
Each mission file is named Mission_YYYYMMDD_HHMMSS.csv and contains:
| Column | Type | Description |
|---|---|---|
| Time | long | Unix timestamp (milliseconds since epoch) |
| Lat | float | Latitude in decimal degrees |
| Lon | float | Longitude in decimal degrees |
| GPS_Alt | float | GPS altitude in meters (backup reference) |
| Baro_Alt_AGL | float | Barometric altitude AGL in meters (primary) |
| Pressure_Pa | float | Raw pressure in Pascals |
| RSSI | int | Signal strength in dBm (null if invalid) |
Example CSV content:
Time,Lat,Lon,GPS_Alt,Baro_Alt_AGL,Pressure_Pa,RSSI
1701423052000,-27.467853,153.027921,45,0.0,101325.0,-85
1701423054000,-27.467855,153.027919,46,12.5,101200.0,-87
1701423056000,-27.467860,153.027915,58,45.2,100850.0,-89The board can be powered with a 3.3V battery. The 3.3V battery can be charged via a USB-C cable.
- A red LED will show when powered.
- An orange LED will show when charging.
- The orange LED will turn off when fully charged and connected via a USB-C cable.
- @vinn-ie - Initial Zephyr board configuration and build setup, plus just a great human and electronics superstar 🚀
Use as you like for your personal use. If you're rich, feel free to buy me a coffee or a HOTA 4-channel battery charger ❤️

